Spring 面试自用

Spring 框架宗旨:

不重新发明技术,让原有技术使用起来更加方便

Spring 几大核心功能

IoC/DI 控制反转/依赖注入
AOP 面向切面编程
声明式事务.

什么是Spring?
Spring是由Rod Johnson组织和开发的一个分层的Java SE/EE full-stack(一站式)轻量级开源框架它以IoC 和AOP 为内核使用基本的JavaBean来完成开发

Spring框架的优点?
1.方便解耦、简化开发: 以将所有对象的创建和依赖关系的维护工作都交给Spring容器管理,大大地降低了组件之间的耦合性。
2.支持AOP:将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性。
3.支持声明式事务处理:配置就可以完成对事务的管理,而无须手动编程。
4.方便程序的测试:提供了对Junit4的支持,可以通过注解方便地测试Spring程序。
5.方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts、Hibernate、MyBatis、Quartz等)的直接支持。
6.· 降低Java EE API的使用难度:Spring对Java EE开发中非常难用的一些API(如JDBC、JavaMail等),都提供了封装,使这些API应用难度大大降低。

Spring框架有哪些主要模块?

Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。
在这里插入图片描述test: spring 提供测试功能
Core Container:核心容器.Spring 启动最基本的条件.

  1. Beans : Spring 负责创建类对象并管理对象
  2. Core: 核心类
  3. Context: 上下文参数.获取外部资源或这管理注解等
  4. SpEl: expression.jar

AOP: 实现 aop 功能需要依赖
Aspects: 切面 AOP 依赖的包
Data Access/Integration : spring 封装数据访问层相关内容

  1. JDBC : Spring 对 JDBC 封装后的代码.
  2. ORM: 封装了持久层框架的代码.例如 Hibernate
  3. transactions:对应 spring-tx.jar,声明式事务使用.

WEB:需要 spring 完成 web 相关功能时需要.

  1. 例如:由 tomcat 加载 spring 配置文件时需要有 spring-web 包

什么是控制反转(IOC)?什么是依赖注入?

IoC:(Inversion of Control, 控制翻转)

把对象的创建、初始化、销毁交给spring来管理,而不是由开发者控制,实现控制反转;
IoC 最大的作用:解耦. 程序员不需要管理对象,解除了对象管理和程序员之间的耦合.

DI:(Dependency Injection, 依赖注入)

当我们一个类需要依赖另一个对象,把另一个对象实例化之后注入给这个对象的过程我们称之为DI

Spring IoC有什么好处呢?

环境搭建

1. 引入依赖:

<properties>
	<spring-version>4.1.6.RELEASE</spring-version>
</properties>
<dependencies>
	<!-- spring -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
		<version>${spring-version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		<version>${spring-version}</version>
	</dependency>
</dependencies>

2. 在 src 下新建 applicationContext.xml

在这里插入图片描述

  1. 文件名称和路径自定义;
  2. applicationContext.xml 的内容会存储到 Spring 的容器 AppliationContext 中;
  3. applicationContext.xml 是基于 schema

schema

  1. schema文件扩展名.xsd
  2. schema理解成DTD的升级版,比DTD具备更好的扩展性.
  3. xmlns是 xsd 文件的 namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
  1. 通过<bean/> 创建对象. 默认配置文件被加载时创建对象
	<bean id="peo" class="com.pojo.People">
	</bean>

3. 使用

getBean(“标签id值”,返回值类型):如果没有第二个参数, 默认是 Object
getBeanDefinitionNames():取得Spring容器中所有管理的所有对象

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

People bean = ac.getBean("peo",People.class);

String[] beanDefinitionNames = ac.getBeanDefinitionNames();

Spring中常见的获取ApplicationContext的实现方式


What is a Spring Bean?

Spring创建对象的三种方式


spring bean是什么?

Bean的作用域:

Spring IoC容器 除了可以实例化、组装和管理的对象,还可以为Bean指定特定的作用域。
Spring 4.3中为Bean的实例定义了7种作用域:
在这里插入图片描述

如何配置 bean 的作用域呢?

1.xml 方式:

	<bean id="teacher3" class="com.pojo.Teacher" scope="prototype"></bean>
	<bean id="teacher4" class="com.pojo.Teacher" scope="singleton"></bean>

2.注解的方式

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

什么是Spring inner beans?:

public class Customer{
	private Person person;
//Setters and Getters
}
public class Person{
	private String name;
	private String address;
	private int age;
//Setters and Getters
}
<bean id="CustomerBean" class="com.somnus.common.Customer">
   <property name="person">
   	<!-- This is inner bean -->
   	<bean class="com.howtodoinjava.common.Person">
   		<property name="name" value="lokesh" />
   		<property name="address" value="India" />
   		<property name="age" value="34" />
   	</bean>
   </property>
</bean>

在这里插入图片描述

Spring框架中的单例Beans是线程安全的么?:

大部分 单例bean 实际都是无状态(没有实例变量)的,(比如 DaoService),这种情况下是线程安全的。

如果你的bean有多种状态的话(比如 View Model 对象), 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。

常见的有两种解决办法

  1. 在 bean 中尽量避免定义可变的成员变量;
  2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式);

在这里插入图片描述

Bean的生命周期:

Spring容器可以管理 singleton作用域 的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。
对于 prototype作用域 的Bean, Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。

Spring容器中,Bean的生命周期流程如图
在这里插入图片描述

  1. 根据配置情况调用Bean构造方法或工厂方法实例化Bean
  2. 利用依赖注入完成Bean中所有属性值的配置注入
  3. 如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值。
  4. 厂实例的引用。
  5. 如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
  6. 如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工操作,这个非常重要,Spring的AOP就是用它实现的。
  7. 如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法。
  8. 如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法。
  9. 如果有BeanPostProcessor和Bean关联,则Spring将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean已经可以被应用系统使用了。
  10. 如果在<bean> 中指定了该Bean的作用范围为scope="singleton",则将该Bean放入SpringIoC的缓存池中,将触发Spring对该Bean的生命周期管理;
    如果在中指定了该Bean的作用范围为scope="prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean。
  11. 如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;
    如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法进行销毁。

IOC 容器对 Bean 的生命周期:

  1. 通过构造器或工厂方法创建 Bean 实例
  2. 为 Bean 的属性设置值和对其他 Bean 的引用
  3. 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessBeforeInitialization 方法
  4. 调用 Bean 的初始化方法(init-method)
  5. 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessAfterInitialization 方法
  6. Bean 可以使用
  7. 当容器关闭时, 调用 Bean 的销毁方法(destroy-method)

在这里插入图片描述

如何给 Bean 的属性赋值(注入)

Bean的装配方式?依赖注入的实现方式?:
spring配置中id和name属性的区别

1. 通过构造方法设置值.

提供带所有参数的有参构造方法

<!-- 设值 注入(通过构造方法) 第一种方式 -->
<bean id="peo" class="com.pojo.People">
		<constructor-arg index="0" value="1"></constructor-arg>
		<constructor-arg index="1" value="张三"></constructor-arg>
		<constructor-arg index="2" value="33"></constructor-arg>
	</bean>
	<bean id="peo1" class="com.pojo.People">
		<constructor-arg name="id" value="2"></constructor-arg>
		<constructor-arg name="name" value="李四"></constructor-arg>
		<constructor-arg name="age" value="35"></constructor-arg>
	</bean>
	<!-- 这样来指定顺序,不然构造方法重载时,会从构造方法最后一个开始寻找,不去看参数的顺序 -->
	<bean id="peo2" class="com.pojo.People">
		<constructor-arg index="0" name="id" value="2"></constructor-arg>
		<constructor-arg index="1" name="name" value="李四"></constructor-arg>
		<constructor-arg index="2" name="age" value="36"></constructor-arg>
	</bean>
//1.构造方法设置值
People bean = ac.getBean("peo",People.class);

2. 设值注入(通过 set 方法)

Bean类必须提供一个默认的无参构造方法。
Bean类必须为需要注入的属性提供对应的setter方法。

如何在Spring中注入一个Java Collection?
Spring提供了以下四种集合类的配置元素:

<list> : 该标签用来装配可重复的list值。
<set> : 该标签用来装配没有重复的set值。
<map>: 该标签可用来注入键和值可以为任何类型的键值对。
<props> : 该标签支持注入键和值都是字符串类型的键值对。

1. 如果属性是基本数据类型String 等简单

<bean id="peo" class="com.bjsxt.pojo.People"> 
	<property name="id" value="222"></property> 
	<property name="name" value="张三"></property> 
</bean>
	<bean id="peo1" class="com.pojo.People">
		<property name="id">
			<value>123</value>
		</property>
		<property name="name">
			<value>"李四"</value>
		</property>
		<property name="age">
			<value>33</value>
		</property>
	</bean>

2. 如果属性是 Set<?>

	<bean id="peopleSetListMapArrayProperty"
		class="com.pojo.PeopleSetListMapArrayProperty">
		<!-- set赋值 -->
		<property name="sets">
			<set>
				<value>qwe</value>
				<value>dsd</value>
			</set>
		</property>
	</bean>

3. 如果属性是 List<?>

3.1 如果 `list` 中就只有一个值
	<bean id="peopleSetListMapArrayProperty"
		class="com.pojo.PeopleSetListMapArrayProperty">
		<!-- list赋值 第一种赋值方式 -->
		<property name="list">
			<list>
				<value>qwe</value>
				<value>www</value>
			</list>
		</property>
		<!-- list赋值 第二种赋值方式 -->
		<property name="list1" value="1,2,3"></property>
	</bean>

4. 如果属性是数组

4.1 如果数组中就只有一个值,可以直接通过 value 属性赋值

	<bean id="peopleSetListMapArrayProperty"
		class="com.pojo.PeopleSetListMapArrayProperty">
		<!-- 数组 -->
		<property name="strs">
			<array>
				<value>1</value>
				<value>2</value>
				<value>3</value>
			</array>
		</property>
	</bean>

5. 如果属性是 map

	<bean id="peopleSetListMapArrayProperty"
		class="com.pojo.PeopleSetListMapArrayProperty">
		<!-- map -->
		<property name="map">
			<map>
				<entry key="a" value="aa"></entry>
				<entry key="b" value="bb"></entry>
				<entry key="c" value="cc"></entry>
			</map>
		</property>
	</bean>

6. 如果属性 Properties 类型

	<bean id="peopleSetListMapArrayProperty"
		class="com.pojo.PeopleSetListMapArrayProperty">
		<!-- property -->
		<property name="prop">
			<props>
				<prop key="key">value</prop>
				<prop key="key1">value1</prop>
				<prop key="key2">value2</prop>
			</props>
		</property>
	</bean>

构造方法注入和设值注入有什么区别?:

设值注入方法支持大部分的依赖注入。对于基本类型,如果我们没有注入的话,可以为基本类型设置默认值
构造方法注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则的话为报错。

3.DI (Dependency Injection) 依赖注入

当一个类(A)中需要依赖另一个类(B)对象时,把 B 赋值给 A 的过 程就叫做依赖注入.

代码体现:

	<bean id="peopleSetListMapArrayProperty"
		class="com.pojo.PeopleSetListMapArrayProperty">
		<!-- 对象 -->
		<property name="desk" ref="dest"></property>
	</bean>

	<bean id="dest" class="com.pojo.Desk">
		<property name="id" value="1"></property>
		<property name="price" value="12"></property>
	</bean>

4.自动注入

1.什么是自动注入

Spring 配置文件中对象名ref="id"id名相同使用自动注入,可以不配置<property/>

	<bean id="teacher" class="com.test.Teacher"></bean>

	<bean id="people" class="com.test.People" autowire="byName">
		<property name="teacher" ref="teacher"></property> <!-- 传统的赋值语句 -->
	</bean>

2.两种配置办法

2.1 在<bean>中通过autowire=” ” 配置,只对这个<bean>生效;
2.2 在<beans>中通过default-autowire=” ”,当前文件中所有<bean>都生效;

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"
	default-autowire="byName">

	<bean id="teacher" class="com.test.Teacher"></bean>

	<bean id="people" class="com.test.People" autowire="byName"></bean>
	<bean id="people1" class="com.test.People" autowire="byType"></bean>
</beans>

3.autowire=“” 可取值

在这里插入图片描述补充:

  1. byType: spring 容器中不可以出现两个相同类型的<bean>
  2. default-autowire=””默认为no
<bean id="teacher" class="com.test.Teacher"></bean>
 people里面包含teacher
<bean id="people" class="com.test.People" autowire="byName"></bean>
<bean id="people" class="com.test.People" autowire="byType"></bean>
<bean id="people" class="com.test.People" autowire="constructor"></bean>
<bean id="people" class="com.test.People" autowire="default"></bean>
People people = ac.getBean("people",People.class);
System.out.println(people);

使用 Spring 简化 MyBatis

1. 导 包

mybatis 所 有 jar 和 spring 基 本 包,spring-jdbc,spring-tx,spring-aop,spring-web,spring 整合 mybatis 的包 等

		<!-- spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-version}</version>
		</dependency>
		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>${mybatis-version}</version>
		</dependency>
		<!-- mybatis和spring整合 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>${mybatis-spring-version}</version>
		</dependency>

2. 先配置 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	version="3.1">

	<!-- tomcat 启动时自动加载spring配置文件 -->

	<!-- 上下文参数 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<!-- spring 配置文件 -->
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>

	<!-- 封装了一个监听器,帮助加载 Spring 的配置文件 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

web.xml里这么配置只是为了使用WebApplicationContext 获取ApplicationContext

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	private UsersService usersService;

	@Override
	public void init() throws ServletException {
		ApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
		usersService = ac.getBean("userService", UsersServiceImpl.class);
	}
}

3. 编写 spring 配置文件 applicationContext.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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 数据源封装类 数据源:获取数据库连接 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="url" value="jdbc:mysql://localhost:3306/ssm"></property>
		<property name="username" value="root"></property>
		<property name="password" value="123456"></property>
	</bean>
	<!-- spring帮助创建sqlsessionfactory对象 -->
	<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 数据库连接信息来源于DataSource -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 扫描器 相当于mybatis下的mapper标签下的package标签  扫描com.mapper后,创建接口对象 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 和factory产生关系 -->
		<property name="sqlSessionFactory" ref="factory"></property>
		<!-- 扫描哪个包 -->
		<property name="basePackage" value="com.mapper"></property>
	</bean>
	
	<!-- 由spring管理service实现类 -->
	<bean id="airportServiceImpl" class="com.service.impl.AirportServiceImpl">
		<property name="airportMapper" ref="airportMapper"></property><!-- mapper包下的类都会在加载配置文件的时候自动创建对象 -->
	</bean>
</beans>

4. 编写代码

1. 正常编写 pojo

public class Airport {
	private int id;
	private String portName;
	private String cityName;
	}

2.编写 mapper 包下时必须使用接口绑定方案注解方案(必须有接口)

package com.mapper;
public interface AirportMapper {
	@Select("select * from airport")
	List<Airport> selAll();
}

3. 正常编写 Service 接口Service 实现类

需要在 Service 实现类中声明 Mapper 接口对象,并生成 get/set 方法

package com.service.impl;
public class AirportServiceImpl implements AirportService {

	private AirportMapper airportMapper;

	public AirportMapper getAirportMapper() {
		return airportMapper;
	}

	public void setAirportMapper(AirportMapper airportMapper) {
		this.airportMapper = airportMapper;
	}

	@Override
	public List<Airport> show() {
		return airportMapper.selAll();
	}
}

4.在 service 中取出 Servie 对象spring 无法管理 Servlet

package com.servlet;
@WebServlet("/airport")
public class AirportServlet extends HttpServlet {
	private AirportService airportService;

	@Override
	public void init() throws ServletException {
		/* 以上还是手动获取配置文件 */
		 //ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

		/*
		 * spring提供一个功能,让tomcat启动时自动获取自动加载spring配置文件,tomcat本身会自动加载web.xml,把配置文件写在web.xml文件里即可。web.xml文件配置参看上文
		 */
		WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
		airportService = ac.getBean("airportServiceImpl", AirportServiceImpl.class);

	}

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setAttribute("list", airportService.show());
		req.getRequestDispatcher("index.jsp").forward(req, resp);
	}
}

在这里插入图片描述

AOP:(Aspect Oriented Programming)面向切面编程

正常程序执行流程都是纵向执行流程
面向切面编程是什么?
在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫做面向切面编程
能够将那些与业务无关,却和业务模块所共同调用的逻辑或责任(例如事务处理日志管理权限控制等)封装起来,
便于减少系统的重复代码
降低模块间的耦合度
并有利于未来的可拓展性和可维护性
原有功能相当于释放了部分逻辑.让职责更加明确

在这里插入图片描述

AOP术语解释:
· Aspect(切面):切面由切入点和通知组成,比如说事务处理和日志处理可以理解为两个切面。
· Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
· Joinpoint(连接点):在Spring中连接点就是被拦截到的方法。
· Pointcut(切入点):那些需要处理的连接点。如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的所有方法都是切入点。
· Advice(通知/增强处理):通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知
· Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
· Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。
在这里插入图片描述

AOP框架:

目前最流行的AOP框架有两个,分别为Spring AOPAspectJ

Spring AOP 属于运行时增强, Spring AOP 基于动态代理(Proxying),在运行期间通过 代理方式向目标类织入增强的代码。

  1. 如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象;
  2. 如果要代理的对象,没有实现接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

AspectJ 编译时增强, AspectJ 基于字节码操作(Bytecode Manipulation)在编译时提供横向代码的织入。

在这里插入图片描述

AOP实现:

1. 基于代理类的AOP实现:

ProxyFactoryBean
ProxyFactoryBean是FactoryBean接口的实现类
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述 在这里插入图片描述

spring 提供了2 种AOP 实现方式

  1. Schema-based
    1.1 每个通知都需要实现接口或类
    1.2 配置spring 配置文件时在<aop:config>配置
  2. AspectJ
    2.1 每个通知不需要实现接口或类
    2.2 配置spring 配置文件是在<aop:config>的子标签<aop:aspect>中配置

在这里插入图片描述

2. Schema-based方式实现:

1. 引入依赖

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${aspectjweaver-version}</version>
		</dependency>

2. 新建通知类

Spring的通知类型:

Spring中的通知按照在目标类方法的连接点位置,可以分为以下5种类型。
· org.aopalliance.intercept.MethodInterceptor(环绕通知)在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
· org.springframework.aop.MethodBeforeAdvice(前置通知)在目标方法执行前实施增强,可以应用于权限管理等功能。
· org.springframework.aop.AfterReturningAdvice(后置通知)在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
· org.springframework.aop.ThrowsAdvice(异常通知)在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
· org.springframework.aop.IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类)。

2.1. 新建前置通知类

arg0: 切点方法对象 Method 对象
arg1: 切点方法参数
arg2:切点在哪个对象中

public class MyBeforeAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
}
2.2新建后置通知类

arg0: 切点方法返回值
arg1:切点方法对象
arg2:切点方法参数
arg3:切点方法所在类的对象

public class MyAfterAdvice implements AfterReturningAdvice{
	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
}
2.3环绕通知(Schema-based 方式)
public class MyArroundAdvice implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		System.out.println("---------------------------------------环绕前置");
		Object proceed = arg0.proceed();
		System.out.println("---------------------------------------环绕后置");
		return proceed;
	}
}
2.4异常通知(Schema-based 方式)

新建一个类实现 throwsAdvice 接口
必须自己写方法,且必须叫 afterThrowing
有两种参数方式:必须是 1 个或 4 个
异常类型要与切点报的异常类型一致

public class MyThrowAdvice implements ThrowsAdvice {
	//spring 倒叙检查方法所以同时存的的时候应该是调用这个方法
	public void afterThrowing(Method m, Object[] args, Object target, Exception ex) {
		System.out.println("执行异常通知 通过schema-base方式" + ex.getMessage());
	}
	public void afterThrowing(Exception ex) throws Throwable {
		System.out.println("执行异常通过-schema-base 方式 ");
	}
}

3.配置 spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!--  配置通知类的对象,在切面中引入  -->
	<bean id="mybeforeadvice" class="com.advice.MyBeforeAdvice"></bean>
	<bean id="myafteradvice" class="com.advice.MyAfterAdvice"></bean>
	<bean id="myArroundAdvice" class="com.advice.MyArroundAdvice"></bean>
	<bean id="myThrowAdvice" class="com.advice.MyThrowAdvice"></bean>
	
	<!--配置Demo类 测试时候使用 -->
	<bean id="demo" class="com.test.Demo"></bean>
	
	<!-- 配置切面 -->
	<!-- schema-based方式配置切点 -->
	<aop:config>
		<!-- * 通配符  匹配任意方法名 任意类名  任意一级包名  -->   
		<aop:pointcut expression="execution(* com.test.Demo.demo1())" id="mypoint1" />
		<aop:pointcut expression="execution(* com.test.Demo.demo2())" id="mypoint2" />
		<aop:pointcut expression="execution(* com.test.Demo.demo3())" id="mypoint3" />
		<aop:pointcut expression="execution(* com.test.Demo.demo4())" id="mypoint4" />
		<!-- 希望匹配任意方法参数方法(..) -->
		<aop:pointcut expression="execution(* com.test.Demo.demo5(..))" id="mypoint5" />
		<aop:pointcut expression="execution(* com.test.Demo.*(..))" id="mypoint" />
		<!-- 通知 -->
		<aop:advisor advice-ref="mybeforeadvice" pointcut-ref="mypoint1" />
		<aop:advisor advice-ref="myafteradvice" pointcut-ref="mypoint2" />
		<aop:advisor advice-ref="myArroundAdvice" pointcut-ref="mypoint3" />
		<aop:advisor advice-ref="myThrowAdvice" pointcut-ref="mypoint4" />
		<aop:advisor advice-ref="mybeforeadvice" pointcut-ref="mypoint5" />
		<aop:advisor advice-ref="myafteradvice" pointcut-ref="mypoint5" />
	</aop:config>

</beans>

在这里插入图片描述

3. 基于XML的声明式AspectJ:

1. 引入依赖

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${aspectjweaver-version}</version>
		</dependency>

2.新建类,类中方法名任意

public class MyAdvice{
	public void mybefore(JoinPoint joinPoint,String name,int age) {
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~前置1");
		System.out.println("前置1"+name+age);
		System.out.println("前置1  目标类:"+joinPoint.getTarget());
		System.out.println("前置1 织入增强类的目标方法:"+joinPoint.getSignature().getName());
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~前置1");
	}
	public void mybefore1(String name) {
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~前置2");
		System.out.println("前置2"+name);
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~前置2");
	}
	public void myafter1(String name) {
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~后置1");
		System.out.println("后置1");
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~后置1");
	}
	public void myafter2(String name) {
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~后置2");
		System.out.println("后置2");
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~后置2");
	}
	public void mythrow(String name) {
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~异常");
		System.out.println("异常");
	}
	public void myexception(Exception e1) {
		System.out.println("执行异常通知"+ e1.getMessage());
	}
	public Object myarround(ProceedingJoinPoint p) throws Throwable {
		System.out.println("~~~~~~~~~~~~~~~环绕前置");
		Object proceed = p.proceed();
		System.out.println("~~~~~~~~~~~~~~~环绕后置");
		return proceed;
	}
}

3.配置spring 配置文件

<aop:aspect>ref属性表示:方法在哪个类中.
<aop: xxxx/> 表示什么通知
method: 当触发这个通知时,调用哪个方法

execution() 括号不能扩上args,中间使用and 不能使用&&springand 解析成&&
args(名称) 名称自定义的顺序和demo1(参数,参数)对应

<aop:before/> arg-names="名称"名称来源于expression=""args(名称),名称数量必须一样

<aop:after/> 后置通知,是否出现异常都执行
<aop:after-returing/> 后置通知,只有当切点正确执行时执行
<aop:after/><aop:after-returing/><aop:after-throwing/>执行顺序和配置顺序有关

throwing: 异常对象名,必须和通知中方法参数名相同

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<bean id="demo" class="com.test.Demo"></bean>
	<bean id="myadvice" class="com.advice.MyAdvice"></bean>
	
	<aop:config>
		<aop:aspect ref="myadvice">
			<aop:pointcut expression="execution(* com.test.Demo.demo1(String,int)) and args(name111,age111)" id="mypoint1"/>
			<aop:pointcut expression="execution(* com.test.Demo.demo2(String)) and args(name11)" id="mypoint2"/>
			<aop:pointcut expression="execution(* com.test.Demo.demo3(String)) and args(name12)" id="mypoint3"/>
			<aop:pointcut expression="execution(* com.test.Demo.demo4(String)) and args(name13)" id="mypoint4"/>
			<aop:pointcut expression="execution(* com.test.Demo.demo5())" id="mypoint5"/>
			<aop:pointcut expression="execution(* com.test.Demo.demo6())" id="mypoint6"/>
			
			<aop:before method="mybefore" pointcut-ref="mypoint1" arg-names="name111,age111"/>
			<aop:before method="mybefore1" pointcut-ref="mypoint2" arg-names="name11"/>
			
			<!-- 不管是否出异常都会走 -->
			<aop:after method="myafter1" pointcut-ref="mypoint3" arg-names="name12"/>
			<!-- 如果出异常就不走了 -->
			<aop:after-returning method="myafter2" pointcut-ref="mypoint4" arg-names="name13"/>
			
			<aop:around method="myarround" pointcut-ref="mypoint5"/>
			
			<aop:after-throwing method="myexception" pointcut-ref="mypoint6" throwing="e1"/>
		</aop:aspect>
	</aop:config>
</beans>

在这里插入图片描述

4.使用注解(基于 Aspect)

在这里插入图片描述

  1. spring 不会自动去寻找注解,必须告诉spring 哪些包下的类中可能有注解
    1.1 引入xmlns:context
<beans 	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
  1. @Component
    2.1 相当于<bean/>
    2.2 @Component(“自定义对象的名称”)
    2.3 如果没有参数,默认对象名称是把类名首字母变小写,相当于<bean id=””/>

实现步骤:

1. 在spring 配置文件中设置注解在哪些包中

spring 不会自动去寻找注解,必须告诉spring 哪些包下的类中可能有注解
1.1 引入xmlns:context

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- spring 不会自动去寻找注解,必须告诉spring 哪些包下的类中可能有注解 -->
	<context:component-scan base-package="com.advice,com.test"></context:component-scan>
	
	<!-- true 使用cglib动态代理  -->
	<!-- false 使用jdk动态代理 -->
	<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

2.在通知类中配置

@Component 类被spring 管理
@Aspect 相当于<aop:aspect/>表示通知方法在当前类中

@Component
@Aspect
public class MyAdvice{
	
	@Pointcut("execution(* com.test.Demo.demo2())")
	private void mypoint2() {}
	
	@Before("mypoint2()")
	public void mybefore2() {
		System.out.println("前置2");
	}
	@After("mypoint2()")
	public void myafter12() {
		System.out.println("后置2");
	}
	
	@Before("com.test.Demo.demo1())")
	public void mybefore() {
		System.out.println("前置");
	}
	@After("com.test.Demo.demo1())")
	public void myafter1() {
		System.out.println("后置1");
	}
	@AfterThrowing("com.test.Demo.demo1())")
	public void mythrow() {
		System.out.println("异常");
	}
	
	@Around("com.test.Demo.demo1())")
	public Object myarround(ProceedingJoinPoint p) throws Throwable {
		System.out.println("环绕前置");
		Object proceed = p.proceed();
		System.out.println("环绕后置");
		return proceed;
	}
}

3. 在Demo类中添加@Componet,在方法上添加@Pointcut(“”) 定义切点

@Component 相当于<bean/>
@Component(“自定义对象的名称”),如果没有参数,默认对象名称是把类名首字母变小写,相当于<bean id=””/>

@Component("demo123")
public class Demo {
	@Pointcut("execution(* com.test.Demo.demo1())")
	public void demo1() {
		System.out.println("demo1");
	}
	public void demo2() {
		System.out.println("demo2");
	}
	public void demo1(String name,int age) {
		System.out.println("demo1"+name+age);
	}
}

在这里插入图片描述

Spring 中常用注解

@Component

@Repository

@Service

@Controller

· @Component: 描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),可以作用在任何层次。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
· @Repository:用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同。
· @Service:   用于将业务层(Service层)的类标识为Spring中的Bean,其功能与@Component相同。
· @Controller:用于将控制层(如Spring MVC的Controller)的类标识为Spring中的Bean,其功能与@Component相同。

使用@Controller注解标注了UserController类,相当于在配置文件中编写
<bean id="userController" class="com.itheima.annotation.UserController"/>

@Component 和 @Bean 的区别是什么?

  1. @Compent 作用就相当于 XML配置
  2. @Bean 需要在配置类中使用,即类上需要加上@Configuration注解
  3. 两者都可以通过@Autowired装配

那为什么有了@Compent,还需要@Bean呢?

如果你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component注解的,因此就不能使用自动化装配的方案了,但是我们可以使用@Bean,当然也可以使用XML配置。

@Scope()

声明 Spring Bean 的作用域

@Bean
@Scope("singleton")
public Person personSingleton() {
    return new Person();
}

@value()

使用 @Value(“${property}”) 读取比较简单的配置信息:

@Value("${lingling}")
String name;

@Autowired

@Qualifier

· @Autowired :按 类型匹配 的方式为属性自动装配合适的依赖对象。
· @Qualifier:若容器中有一个以上类型相匹配的Bean时,可以使用@Qualifier指定所需的Bean的名称。

在这里插入图片描述

@Resource

· @Resource:默认按byName 进行装配。

使用了@Resource注解标注在userService属性上,相当于在配置文件中编写
<property name="userService" ref="userService" />

@Resource中有两个重要属性:name和type。
Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。
如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;
如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;
如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。

@Autowired和@Resource的区别是什么

@Resource   默认byname  属于JDk
@Autowired  默认bytype  属于Spring

@Aspect() 定义切面类

@Pointcut() 定义切点

@Before() 前置通知

@After 后置通知

@AfterReturning 后置通知,必须切点正确执行

@Arround 环绕通知

@AfterThrowing 异常通知

@Component
@Aspect
public class MyAdvice{
	
	@Pointcut("execution(* com.test.Demo.demo2())")
	private void mypoint2() {}
	
	@Before("mypoint2()")
	public void mybefore2() {
		System.out.println("前置2");
	}
	@After("mypoint2()")
	public void myafter12() {
		System.out.println("后置2");
	}
	
	@Before("com.test.Demo.demo1())")
	public void mybefore() {
		System.out.println("前置");
	}
	@After("com.test.Demo.demo1())")
	public void myafter1() {
		System.out.println("后置1");
	}
	@AfterThrowing("com.test.Demo.demo1())")
	public void mythrow() {
		System.out.println("异常");
	}
	
	@Around("com.test.Demo.demo1())")
	public Object myarround(ProceedingJoinPoint p) throws Throwable {
		System.out.println("环绕前置");
		Object proceed = p.proceed();
		System.out.println("环绕后置");
		return proceed;
	}
}

在这里插入图片描述

Spring 中加载 properties 文件

  1. src 下新建 xxx.properties 文件
key=values
  1. 配置 spring配置文件
<beans xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 注解扫描 -->
    <context:component-scan base-package="com.service.impl"></context:component-scan>
    <!-- 加载属性文件 -->
    <context:property-placeholder location="classpath:db.properties,classpath:field.properties"/>

	@Value("${key}")
	private String str;

在这里插入图片描述

事务管理的方式:

1. 编程式事务:

由程序员编程事务控制代码,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。

2. 声明式事务:

事务控制代码已经由spring 写好.程序员只需要声明出 哪些方法 需要进行事务控制和 如何进行 事务控制.

声明式事务

声明式事务中属性解释

<tx:advice id="txadvice" transaction-manager="txmanager">
	<tx:attributes>
		<!-- 所有以ins开头都添加事务 -->
		<tx:method name="ins*" propagation="REQUIRED" />
		<tx:method name="upd*" isolation="SERIALIZABLE" />
		<tx:method name="del*" rollback-for="java.lang.Exception" />
		<tx:method name="*" read-only="true" />
	</tx:attributes>
</tx:advice>

1.name 哪些方法需要有事务控制,支持*通配符
2. readonly=”boolean” 是否是只读事务.

  • 如果为true,告诉数据库此事务为只读事务.数据会优化,会对性能有一定提升;所以只要是查询的方法,建议使用此数据;
  • 如果为false(默认值),事务需要提交的事务.建议新增,删除,修改.

3.propagation 控制事务传播行为;
4.isolation 控制事务隔离级别;
事务隔离级别与传播行为

  1. rollback-for=”异常类型全限定路径”
    • 当出现什么异常时需要进行回滚
    • 建议:给定该属性值.
    • 手动抛异常一定要给该属性值.
  2. no-rollback-for=””
  • 当出现什么异常时不滚回事务.

1. 基于XML方式的声明式事务:

<!-- spring-jdbc中 -->
<bean id="txmanager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txadvice" transaction-manager="txmanager">
	<tx:attributes>
		<!-- 哪个方法需要添加事务 -->
		<!-- 所有以ins开头都添加事务 -->
		<tx:method name="ins*" propagation="REQUIRED" />
		<tx:method name="upd*" isolation="SERIALIZABLE" />
		<tx:method name="del*" rollback-for="java.lang.Exception" />
		<tx:method name="*" read-only="true" />
	</tx:attributes>
</tx:advice>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<aop:config>
	<!-- 切点范围设计大点 -->
	<aop:pointcut expression="execution(* com.service.impl.*.*(..))" id="mypoint" />
	<aop:advisor advice-ref="txadvice" pointcut-ref="mypoint" />
</aop:config>
public interface UsersMapper {
	int insUsers(Users users);
}
<mapper namespace="com.mapper.UsersMapper">
	<insert id="insUsers" parameterType="users">
		insert into users values(default, #{username},#{password})
	</insert>
</mapper>
public class UsersServiceImpl implements UsersService {
	private UsersMapper usersMapper;

	public UsersMapper getUsersMapper() {
		return usersMapper;
	}

	public void setUsersMapper(UsersMapper usersMapper) {
		this.usersMapper = usersMapper;
	}

	@Override
	public int insUsers(Users user) {
		return usersMapper.insUsers(user);
	}
}
UsersServiceImpl usersServiceImpl = ac.getBean("usersServiceImpl",UsersServiceImpl.class);
Users u = new Users();
u.setUsername("李四2");
u.setPassword("123");
usersServiceImpl.insUsers(u);

在这里插入图片描述

2. 基于Annotation方式的声明式事务:

这种方式的使用非常简单,开发者只需做两件事情:
① 在Spring容器中注册事务注解驱动。
② 在需要使用事务的Spring Bean类或者Bean类的方法上添加注解@Transactional。
在这里插入图片描述在这里插入图片描述

动态代理:

我们已经知道AOP中的代理就是由AOP框架动态生成的一个对象,该对象可以作为目标对象使用。Spring中的AOP代理,可以是JDK动态代理,也可以是CGLIB代理。

JDK动态代理:
JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。

CGLIB代理:
JDK动态代理的使用非常简单,但它还有一定的局限性,使用动态代理的对象必须实现一个或多个接口。如果要对没有实现接口的类进行代理,那么可以使用CGLIB代理。
CGLIB(Code Generation Library)对指定的目标类生成一个子类,并对子类进行增强。

在这里插入图片描述

Spring 框架中都用到了哪些设计模式?

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值