关于Spring

本文详细解析Spring框架中的控制反转(IoC)和面向切面编程(AOP)核心概念,涵盖XML和注解配置方式,包括依赖注入、BeanFactory与ApplicationContext区别、动态代理、AOP术语及配置等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、控制反转-Inversion Of Control

1.关于IOC

业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种编译期依赖关系,应该在我们开发中杜绝。在应用加载时,创建一个 Map,用于存放三层对象。我们获取对象时,可以直接跟工厂要,有工厂为我们查找或者创建对象。是被动的。

2.基于 XML 的配置

配置文件:

<!-- bean标签:用于配置让spring创建对象,并且存入ioc容器之中
id 属性:对象的唯一标识。
class 属性:指定要创建对象的全限定类名
-->
<!-- 配置 service -->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> </bean>
<!-- 配置 dao -->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>

测试类:

public static void main(String[] args) {
//1.使用 ApplicationContext 接口,就是在获取 spring 容器
	ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.根据 bean 的 id 获取对象
	IAccountService aService = (IAccountService) ac.getBean("accountService"); System.out.println(aService);
	IAccountDao aDao = (IAccountDao) ac.getBean("accountDao"); System.out.println(aDao);
}

先通过ApplicationContext指定spring容器(也就是那个配置文件),然后直接用getBean(bean的id)从spring中获得想要的对象,就不用new了。

3.BeanFactory 和 ApplicationContext 的区别

ApplicationContext是BeanFactory的子接口。大部分情况下都使用ApplicationContext。
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。 BeanFactory:什么使用什么时候创建对象。

4.什么是依赖注入

依赖注入:Dependency Injection。它是spring框架核心ioc的具体实现。 我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。
ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。也就是我们告诉spring容器,我们需要的这个对象(A)里面又需要哪些成员(可以是基本数据类型和 String 类型,也可以是其他bean类型比如对象B),spring容器帮我们把这些成员注入到A之后再交给我们A,A、B都需要提前放在spring容器中。

5.通过配置文件注入

1.通过构造函数注入

<!-- 使用构造函数的方式,给 service 中的属性传值 
	要求:类中需要提供一个对应参数列表的构造函数。 
	涉及的标签:constructor-arg
	属性: index:指定参数在构造函数参数列表的索引位置 
		 type:指定参数在构造函数中的数据类型
		 name:指定参数在构造函数中的名称 用这个找给谁赋值
		  =======上面三个都是找给谁赋值,下面两个指的是赋什么值的============== 
		 value:它能赋的值是基本数据类型和 String 类型
		 ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
	<constructor-arg name="name" value="张三"></constructor-arg> 	
	<constructor-arg name="age" value="18"></constructor-arg>
	<constructor-arg name="birthday" ref="now"></constructor-arg> 
<bean id="now" class="java.util.Date">

2.通过set方法注入(常用!!!)

<!-- 通过配置文件给 bean 中的属性传值:使用 set 方法的方式 涉及的标签:
property
	属性:name:找的是类中 set 方法后面的部分 (setName的name,setAge的age)
		ref:给属性赋值是其他 bean 类型的 
		value:给属性赋值是基本数据类型和 string 类型的
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
	<property name="name" value="test"></property> 
	<property name="age" value="21"></property> 
	<property name="birthday" ref="now"></property>
<bean id="now" class="java.util.Date">

6.基于注解的IOC

解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。当我们使用注解注入时,set 方法不用写。

7.创建 spring 的 xml 配置文件并开启对注解支持

<!-- 告知 spring 创建容器时要扫描的包 -->
<context:component-scan base-package="com.itheima"></context:component-scan>

8.常用注解

1.用于创建对象的,相当于:bean id="" class=""
@Component
value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
@Controller: 一般用于表现层的注解。
@Service: 一般用于业务层的注解。
@Repository: 一般用于持久层的注解。
他们的作用及属性和@Component都是一模一样的。
2.用于注入数据的,相当于:property name="" ref="“或value=”"
@Autowired
自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个 类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到 就报错。
@Value: 注入基本数据类型和 String 类型数据的

9.spring 的纯注解配置

我们发现,之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:

<context:component-scan base-package="com.itheima"></context:component-scan>

如果他要也能用注解配置,那么我们就离脱离 xml 文件又进了一步。
另外,数据源和 JdbcTemplate 的配置也需要靠注解来实现。
@Configuration: 用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。
@ComponentScan: 用于指定 spring 在初始化容器时要扫描的包。

@Configuration
 @ComponentScan("com.itheima") public class SpringConfiguration {
}

指明这是一个配置类,spring初始化容器时要扫描的包是”com.itheima“。
@Bean: 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。

@Bean(name="dbAssit")
	public createDBAssit(DataSource dataSource) { 
		return new DBAssit(dataSource);
}

@Component作用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean。而@Bean是作用在方法上的,告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。
@PropertySource: 用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到 properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?
@Import: 用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。

@Configuration
@ComponentScan(basePackages = "com.itheima.spring") 
@Import({ JdbcConfig.class})
public class SpringConfiguration {
}
@Configuration //可以不写
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig{
	@Value("${jdbc.driver}")
	private String driver; @Value("${jdbc.url}") private String url; 
	@Value("${jdbc.username}") private String username;
	@Value("${jdbc.password}") private String password;
	/**
	* 创建一个数据源,并存入 spring 容器中 * @return
	*/
	@Bean(name="dataSource")
	public DataSource createDataSource() {
		try {
			ComboPooledDataSource ds = new ComboPooledDataSource(); 
			ds.setDriverClass(driver);
			ds.setJdbcUrl(url);
			ds.setUser(username); 
			ds.setPassword(password); return ds;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} }
 	}
 }

jdbc.properties 文件:

jdbc.driver=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql://localhost:3306/day44_ee247_spring
jdbc.username=root
jdbc.password=1234

10.通过注解获取容器

 ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

二、AOP

1.AOP概述

AOP:全称是 Aspect Oriented Programming 即:面向切面编程。简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的 基础上,对我们的已有方法进行增强。

2.动态代理常用的两种方式

1.基于接口的动态代理:
提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。
在很久以前,演员和剧组都是直接见面联系的。没有中间人环节。
而随着时间的推移,产生了一个新兴职业:经纪人(中间人),这个时候剧组再想找演员就需要通过经纪人来找了。
2.基于子类的动态代理
提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。

3.Spring 中的 AOP

1.相关术语
Joinpoint(连接点 ):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。就是指对哪些方法进行拦截。
Pointcut(切入点 ):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。就是指要对哪些方法进行增强。
Advice(通知/增强):所谓通知是指拦截到Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。就是在对某个方法的一些增强。
Introduction(引介 ):引介是一种特殊的通知在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或 Field。
Target(目标对象 ):代理的目标对象。
Weaving(织入 ): 是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。 Proxy(代理) :一个类被 AOP 织入增强后,就产生一个结果代理类。 Aspect(切面 ):是切入点和通知(引介)的结合。

4.基于 XML 的 AOP 配置

1.前置通知、后置通知、异常通知、最终通知
先配置spring的ioc,然后准备一个通知类(增强类),里面是需要制作成通知的信息。

public class TransactionManager {
	//定义一个 DBAssit
	private DBAssit dbAssit ;
	public void setDbAssit(DBAssit dbAssit) { this.dbAssit = 	dbAssit;
	}
  	//开启事务(前置)
	public void beginTransaction() { 
		try {
			dbAssit.getCurrentConnection().setAutoCommit(false); 
		} catch (SQLException e) {
			e.printStackTrace();
		} 
	}
	//提交事务(后置)
	public void commit() {
		try { 
			dbAssit.getCurrentConnection().commit();
		} catch (SQLException e) {
			e.printStackTrace(); 
		}
	}
	//回滚事务(异常)
	public void rollback() { 
		try {
			dbAssit.getCurrentConnection().rollback(); 
		} catch (SQLException e) {
			e.printStackTrace(); 
		}
	}
	//释放资源(最终)
	public void release() { 
		try {
			dbAssit.releaseConnection(); 
		} catch (Exception e) {
			e.printStackTrace(); 
		}
	}
}

先把通知类放到ioc容器中

<bean id="txManager" class="com.itheima.utils.TransactionManager">
	<property name="dbAssit" ref="dbAssit"></property>
</bean>

然后配置aop

<aop:config>
	<aop:aspect id="txAdvice" ref="txManager">
		<!--配置通知的类型要写在此处-->
		<!--用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。-->
		<aop:pointcut expression="execution(public void com.itheima.service.impl.AccountServiceImpl.transfer( java.lang.String, java.lang.String, java.lang.Float))" id="pt1"/>
		<!--配置前置通知,在切入点方法执行之前执行-->
		<aop:before method="beginTransaction" pointcut-ref="pt1"/>
		<!--配置后置通知,切入点方法正常执行之后。它和异常通知只能有一个执行-->
		<aop:after-returning method="commit" pointcut-ref="pt1"/>
		<!--配置异常通知,它和后置通知只能执行一个-->
		<aop:after-throwing method="rollback" pointcut-ref="pt1"/>
		<!--配置最终通知,无论切入点方法执行时是否有异常,它都会在其后面执行-->
		<aop:after method="release" pointcut-ref="pt1"/>
	</aop:aspect>
</aop:config>

3.环绕通知
配置环绕通知

<aop:config>
	<aop:pointcut expression="execution(*com.itheima.service.impl.*.*(..))" id="pt1"/>
	<aop:aspect id="txAdvice" ref="txManager">
		<!-- 配置环绕通知 -->
		<aop:around method="transactionAround" pointcut-ref="pt1"/>
    </aop:aspect>
</aop:config>

在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。

public Object transactionAround(ProceedingJoinPoint pjp) {
	//定义返回值
	Object rtValue = null;
	try {
		//获取方法执行所需的参数
		Object[] args = pjp.getArgs(); //前置通知:开启事务
		beginTransaction();
		//执行方法
		rtValue = pjp.proceed(args); //后置通知:提交事务
		commit();
	}catch(Throwable e) { 
		//异常通知:回滚事务 rollback(); 
		e.printStackTrace();
	}finally { 
		//最终通知:释放资源 
		release();
	}
	return rtValue;
}

5.基于注解的 AOP 配置

1.前置通知、后置通知、异常通知、最终通知
把通知类也使用注解配置,在通知类上使用@Aspect 注解声明为切面(切面类就是用来给别的类增强方法的类)。

@Component("txManager") 
@Aspect//表明当前类是一个切面类 
public class TransactionManager {
	//定义一个 DBAssit 
	@Autowired
	private DBAssit dbAssit ;

	//开启事务
	@Before("execution(* com.itheima.service.impl.*.*(..))") 		//前置通知
	public void beginTransaction() {
		try { 
			dbAssit.getCurrentConnection().setAutoCommit(false);
		} catch (SQLException e) { 
			e.printStackTrace();
		} 
	}
	//提交事务
	@AfterReturning("execution(* com.itheima.service.impl.*.*(..))") 		//后置通知
	public void commit() {
		try { 
			dbAssit.getCurrentConnection().commit();
		} catch (SQLException e) { 
			e.printStackTrace();
		}
	}
	//回滚事务
	@AfterThrowing("execution(* com.itheima.service.impl.*.*(..))") 		//异常通知
	public void rollback() {
		try { 
			dbAssit.getCurrentConnection().rollback();
		} catch (SQLException e) {
			e.printStackTrace(); 
		}
	}
	//释放资源
	@After("execution(* com.itheima.service.impl.*.*(..))") 		//最终通知
	public void release() {
		try {
			dbAssit.releaseConnection(); 
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

最后在 spring 配置文件中开启 spring 对注解 AOP 的支持

<aop:aspectj-autoproxy/>

2.环绕通知

@Around("execution(* com.itheima.service.impl.*.*(..))") 
public Object transactionAround(ProceedingJoinPoint pjp) {
	//定义返回值
	Object rtValue = null; 
	try {
		//获取方法执行所需的参数
		Object[] args = pjp.getArgs(); 
		//前置通知:开启事务 		
		beginTransaction();
		//执行方法
		rtValue = pjp.proceed(args); 
		//后置通知:提交事务
		commit();
	}catch(Throwable e) {
		//异常通知:回滚事务 
		rollback();
		e.printStackTrace();
	}finally {
		//最终通知:释放资源 
		release();
	}
	return rtValue; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值