SpringIOC与AOP的总结

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

一. IoC的概念

IoC–Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想。
Ioc是指在程序开发过程中,实例的创建不再由调用者管理,而是由Spring容器管理。Spring容器会负责控制程序之间的关系,而不是由程序代码直接控制,因此,控制权由程序代码转移到了Spring容器中,控制权发生了反转,这就是IoC的思想。

获取Spring容器: Spring 提供了两种IoC容器,分别为BeanFactory和ApplicationContext

  1. BeanFactory
    BeanFactory 是基础类型的 IoC 容器,是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。 BeanFactory 接口有多个实现类,最常见的是org.Springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配
    Bean 的。

BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(Spring配置文件的名称));

1.1 ApplicationContext

ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。

1.1.1 ApplicationContext 接口有两个常用的实现类:
1.1.1.1 ClassPathXmlApplicationContext——常用

该类从类路径 ClassPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作

ApplicationContext applicationContext=new ClassPathXmlApplicationContext(Spring配置文件的名称);

1.1.1.2 FileSystemXmlApplicationContext

ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);

它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,
FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件的位
置,它可以获取类路径之外的资源,如“D:\application.xml”。

1.2 Spring容器创建对象的方式

1.2.1 使用默认的构造方法
1.2.2 使用带参数的构造方法
public Team(Integer id, String name, String location) {
  this.id = id;
  this.name = name;
  this.location = location;
  System.out.println("Team - 带参数的构造方法
  id="+id+",name="+name+",location="+location);
}
1.2.3 使用工厂类
package com.pojo;
public class MyFactory {
/**
* 实例方法
* @return
*/
  public Team instanceFun(){
    System.out.println("MyFactory------instanceFun");
    return new Team(1003,"湖人","洛杉矶");
  }
/**
* 静态方法
* @return
*/
  public static Team staticFun(){
    System.out.println("MyFactory------staticFun");
    return new Team(1004,"小牛","达拉斯");
  }
  public static void main(String[] args) {
    Team team1 = MyFactory.staticFun();
    MyFactory factory=new MyFactory();
    Team team = factory.instanceFun();
  }
}
1.2.4 创建新的配置文件createType.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--spring容器创建对象的方式:
1、通过默认构造方法测试类:
2、通过带参数的构造方法
3、通过工厂方法:实例方法,静态方法-->
<!--1、通过默认构造方法-->
<bean id="team1" class="com.kbb.pojo.Team"></bean>
<!-- 2、通过带参数的构造方法-->
<bean id="team2" class="com.kbb.pojo.Team">
	<!--name:表示参数的名称-->
	<constructor-arg name="id" value="1001"/>
	<constructor-arg name="name" value="勇士"/>
	<constructor-arg name="location" value="金州"/>
</bean>
<bean id="team3" class="com.kbb.pojo.Team">
	<!--index:表示参数的下标索引-->
	<constructor-arg index="0" value="1002"/>
	<constructor-arg index="1" value="热火"/>
	<constructor-arg index="2" value="迈阿密"/>
</bean>
<!--3、通过工厂方法:
3.1 静态方法
Team team1 = MyFactory.staticFun();-->
<bean id="staticTeam" class="com.kbb.pojo.MyFactory" factorymethod="staticFun"></bean>
<!--3、通过工厂方法:
3.2 实例方法
MyFactory factory=new MyFactory();
Team team = factory.instanceFun();-->
<bean id="factory" class="com.kbb.pojo.MyFactory"></bean>
<bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun">
</bean>
</beans>
1.2.5 测试类
package com.kkb.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CreateTypeTest {
	@Test
	public void test01(){
		ApplicationContext ac=new ClassPathXmlApplicationContext("createType.xml");
    }
}

1.3 基于XML的DI

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即

由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为
了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过
简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资
源来自何处,由谁实现。
IoC 是一个概念,是一种思想,其实现方式多种多样。依赖注入就是其中用的比较多的一种方式。
Ioc和DI是同一个概念的不同角度描述。 IoC是一种思想,概念,DI是实现它的手段。Spring框架使用依赖注入实现IoC。
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。
Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依
赖关系。使用 IoC 实现对象之间的解耦和。

1.3.1 注入分类

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完
成的,称为注入。

1.3.1.1 通过set方法

set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在
Spring 的依赖注入中大量使用。

1.3.1.2 通过构造方法

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。使用构造器设置依赖关系。

1.3.1.3 自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入
byType: 根据类型自动注入
1、 byName
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,
让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的
被调用者 bean 的 id 进行比较而实现自动注入的。
2、 byType
使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用
者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样
的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

二. Spring核心之AOP

2.1 什么是AOP

AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态
代理实现程序功能的统一维护的一种技术。
AOP的作用:不修改源码的情况下,程序运行期间对方法进行功能增强
好处:
1、减少代码的重复,提高开发效率,便于维护。
2、专注核心业务的开发。
核心业务和服务性代码混合在一起
开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。
通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。

2.2 AOP的实现机制-动态代理

2.2.1 什么是代理模式

代理:自己不做,找人帮你做。
代理模式:在一个原有功能的基础上添加新的功能。
分类:静态代理和动态代理。

2.3 静态代理

2.3.1 原有方式:核心业务和服务方法都编写在一起
package com.service;
public class TeamService {
	public void add(){
		try {
			System.out.println("开始事务");
			System.out.println("TeamService---- add----");// 核心业务
			System.out.println("提交事务");
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("回滚事务");
		}
	}
}
2.3.2 基于类的静态代理

将服务性代码分离出来,核心业务–保存业务中只有保存功能

package com.staticproxy;
import com.service.TeamService;
/**
* 基于类的静态代理:
* 要求继承被代理的类
* 弊端:每次只能代理一个类
*/
public class ProxyTeamService extends TeamService {
	public void add(){
		try {
			System.out.println("开始事务");
			super.add();//核心业务就是由被代理对象完成;其他服务功能由代理类完成
			System.out.println("提交事务");
		}catch (Exception e){
			System.out.println("回滚事务");
		}
	}
}

public static void main(String[] args) {
	TeamService ser=new ProxyTeamService();
	ser.add();
}
2.3.3 基于接口的静态代理

为核心业务(保存add)创建一个接口,通过接口暴露被代理的方法
要求:代理类和被代理类都实现了同一个接口

package com.service;
/**
* 接口定义核心方法
*/
public interface IService {
  void add();
}
package com.service;
public class TeamService implements IService{
	@Override
	public void add(){
		System.out.println("TeamService---- add----");// 核心业务
	}
} 

package com.service;
public class UserService implements IService{
	@Override
	public void add() {
		System.out.println("UserService---- add-----");
	}
}
package com.staticproxy;
import com.service.IService;
/**
* 基于接口的静态代理:
* 代理类和被代理类实现同一个接口
*/
public class ProxyTranService implements IService {
	private IService service;//被代理的对象
	public ProxyTranService(IService service) {
		this.service = service;
	} 
	@Override
	public void add() {
		try {
			System.out.println("开始事务");
			service.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成
			System.out.println("提交事务");
		}catch (Exception e){
			System.out.println("回滚事务");
		}
	}
}
package com.staticproxy;
import com.service.IService;
public class ProxyLogService implements IService {
	private IService service;//被代理对象
	public ProxyLogService(IService service) {
		this.service = service;
	}
	@Override
	public void add() {
		try {
			System.out.println("开始日志");
			service.add();//核心业务就是由被代理对象完成 ;其他服务功能由代理类完成
			System.out.println("结束日志");
		}catch (Exception e){
			System.out.println("异常日志");
		}
	}
}

测试类:

public static void main(String[] args) {
	TeamService teamService = new TeamService();//被代理对象
	UserService userService = new UserService();//被代理对象
	ProxyTranService tranService = new ProxyTranService(userService);//事务代理
	对象--一级代理
	//tranService.add();//代理对象干活
	ProxyLogService logService = new ProxyLogService(tranService);//日志的代理对--二级代理
	logService.add();
}

总结静态代理:
1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。
2)缺点:
因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。
一旦接口增加方法,目标对象与代理对象都要维护。

2.4 动态代理

静态代理:要求代理类一定存在,
动态代理:程序运行的时候,根据要被代理的对象动态生成代理类。
类型:
1、基于JDK的动态代理
2、基于CGLIB的动态代理

2.4.1 基于JDK的动态代理
/**
*newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
ClassLoader :类加载器,因为动态代理类,借助别人的类加载器。一般使用被代理对象的类加载器。
Class<?>[] interfaces:接口类对象的集合,针对接口的代理,针对哪个接口做代理,一般使用的就是被
代理对象的接口。
InvocationHandler:句柄,回调函数,编写代理的规则代码
public Object invoke(Object arg0, Method arg1, Object[] arg2)
Object arg0:代理对象
Method arg1:被代理的方法
Object[] arg2:被代理方法被执行的时候的参数的数组
*/
package com.dynamicproxy;
import com.service.IService;
import com.service.TeamService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyJDKProxy {
	public static void main(String[] args) {
		//目标对象--被代理对象
		TeamService teamService = new TeamService();
		//返回代理对象 调用JDK中Proxy类中的静态方法newProxyInstance获取动态代理类的实例
		IService proxyService = (IService) Proxy.newProxyInstance(
		teamService.getClass().getClassLoader(),
		teamService.getClass().getInterfaces(),
		new InvocationHandler() {  //回调函数 编写代理规则
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					try {
						System.out.println("开始事务");
						Object invoke = method.invoke(teamService, args);//核心业务
						System.out.println("提交事务");
						return invoke;
					}catch (Exception e){
						System.out.println("回滚事务");
						e.printStackTrace();
						throw e;
					}finally {
						System.out.println("finally---------");
					}
				}
			}
		);
		//代理对象干活
		proxyService.add();
		System.out.println(teamService.getClass());
		System.out.println(proxyService.getClass()+"--------");
	}
}

代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能用JDK动态代理
如果想要功能扩展,但目标对象没有实现接口,怎样功能扩展?
子类的方式实现代理CGLIB。

2.4.2 基于CGLIB的动态代理

Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有
    实现接口的类,就可以使用CGLIB实现。
  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的
    被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception。
  • CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓
    励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉
2.4.2.1 直接编写测试类
package com.cglibproxy;
public class NBAService {
	public int add(String name,int id){
		System.out.println("NBAService---- add----");
		return id;
	}
}

***************************************

public static void main(String[] args) {
	//目标对象:没有接口
	NBAService nbaService = new NBAService();
	//创建代理对象:选择cglib动态代理
	NBAService proxyService = (NBAService)Enhancer.create(nbaService.getClass(),new MethodInterceptor() {
		//回调函数编写代理规则
		@Override
		public Object intercept(Object o, Method method, Object[] objects,
		MethodProxy methodProxy) throws Throwable {
				try {
					System.out.println("开始事务");
					Object invoke = methodProxy.invokeSuper(o, objects);//核心
					System.out.println("提交事务");
					return invoke;
				} catch (Exception e){
					System.out.println("事务回滚");
					throw e;
				}finally {
				System.out.println("finally------------");
			}
		}
	});
	//代理对象干活
	int res=proxyService.add("huren",1001);
	System.out.println(res);
}

三. SpringAOP

3.1 Spring AOP相关概念

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分
进行代码编写,并通过配置的方式完成指定目标的方法增强。
我们先来介绍AOP的相关术语:

  • Target(目标对象)
    要被增强的对象,一般是业务逻辑类的对象。
  • Proxy(代理)
    一个类被 AOP 织入增强后,就产生一个结果代理类。
  • Aspect(切面)
    表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。
  • Joinpoint(连接点)
    所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法(一般是类中的业务方法),因为
    Spring只支持方法类型的连接点。
  • Pointcut(切入点)
    切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
    被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
  • Advice(通知/增强)
    所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时
    间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
    通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
    切入点定义切入的位置,通知定义切入的时间。
  • Weaving(织入).
    是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编
    译期织入和类装载期织入。
    切面的三个关键因素:
    1、切面的功能–切面能干啥
    2、切面的执行位置–使用Pointcut表示切面执行的位置
    3、切面的执行时间–使用Advice表示时间,在目标方法之前还是之后执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值