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 启动最基本的条件.
- Beans : Spring 负责创建类对象并管理对象
- Core: 核心类
- Context: 上下文参数.获取外部资源或这管理注解等
- SpEl: expression.jar
AOP: 实现 aop 功能需要依赖
Aspects: 切面 AOP 依赖的包
Data Access/Integration : spring 封装数据访问层相关内容
- JDBC : Spring 对 JDBC 封装后的代码.
- ORM: 封装了持久层框架的代码.例如 Hibernate
- transactions:对应 spring-tx.jar,声明式事务使用.
WEB:需要 spring 完成 web 相关功能时需要.
- 例如:由 tomcat 加载 spring 配置文件时需要有 spring-web 包
什么是控制反转(IOC)?什么是依赖注入?
IoC:(Inversion of Control, 控制翻转)
把对象的创建、初始化、销毁交给spring来管理,而不是由开发者控制,实现控制反转;
IoC 最大的作用:解耦
. 程序员不需要管理对象,解除了对象管理和程序员之间的耦合.
DI:(Dependency Injection, 依赖注入)
当我们一个类需要依赖另一个对象,把另一个对象实例化之后注入给这个对象的过程我们称之为DI
环境搭建
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
- 文件名称和路径自定义;
applicationContext.xml
的内容会存储到Spring
的容器AppliationContext
中;applicationContext.xml
是基于schema
;
schema
schema
文件扩展名.xsd
- 把
schema
理解成DTD
的升级版,比DTD
具备更好的扩展性.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>
- 通过
<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
实际都是无状态(没有实例变量)的,(比如 Dao
、Service
),这种情况下是线程安全的。
如果你的bean
有多种状态的话(比如 View Model 对象
), 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。
常见的有两种解决办法
- 在 bean 中尽量避免定义可变的成员变量;
- 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式);
Bean的生命周期:
Spring容器可以管理 singleton作用域 的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。
对于 prototype作用域 的Bean, Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。
Spring容器中,Bean的生命周期流程如图
- 根据配置情况调用Bean构造方法或工厂方法
实例化Bean
。 - 利用依赖注入完成Bean中
所有属性值的配置注入
。 - 如果Bean实现了
BeanNameAware
接口,则Spring调用Bean的setBeanName()
方法传入当前Bean的id值。 - 厂实例的引用。
- 如果Bean实现了
ApplicationContextAware
接口,则Spring调用setApplicationContext()
方法传入当前ApplicationContext实例的引用。 - 如果
BeanPostProcessor
和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()
对Bean进行加工操作,这个非常重要,Spring的AOP就是用它实现的。 - 如果Bean实现了
InitializingBean
接口,则Spring将调用afterPropertiesSet()
方法。 - 如果在配置文件中通过
init-method
属性指定了初始化方法,则调用该初始化方法。 - 如果有
BeanPostProcessor
和Bean关联,则Spring将调用该接口的初始化方法postProcessAfterInitialization()
。此时,Bean已经可以被应用系统使用了。 - 如果在
<bean>
中指定了该Bean的作用范围为scope="singleton"
,则将该Bean放入SpringIoC的缓存池中,将触发Spring对该Bean的生命周期管理;
如果在中指定了该Bean的作用范围为scope="prototype"
,则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean。 - 如果Bean实现了
DisposableBean
接口,则Spring会调用destory()
方法将Spring中的Bean销毁;
如果在配置文件中通过destory-method
属性指定了Bean的销毁方法,则Spring将调用该方法进行销毁。
IOC 容器对 Bean 的生命周期:
- 通过构造器或工厂方法
创建 Bean 实例
- 为 Bean 的
属性设置值
和对其他 Bean 的引用 - 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的
postProcessBeforeInitialization
方法 - 调用 Bean 的初始化方法(
init-method
) - 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的
postProcessAfterInitialization
方法 - Bean 可以
使用
了 - 当容器关闭时, 调用 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=“” 可取值
补充:
byType
:spring
容器中不可以出现两个相同类型的<bean>
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 AOP
和AspectJ
。
Spring AOP
属于运行时增强, Spring AOP 基于动态代理(Proxying),在运行期间通过 代理方式向目标类织入增强的代码。
- 如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用
JDK Proxy
,去创建代理对象; - 如果要代理的对象,没有实现接口,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用
Cglib
生成一个被代理对象的子类来作为代理。
AspectJ
编译时增强, AspectJ 基于字节码操作(Bytecode Manipulation)在编译时提供横向代码的织入。
AOP实现:
1. 基于代理类的AOP实现:
ProxyFactoryBean
ProxyFactoryBean是FactoryBean接口的实现类
spring 提供了2 种AOP 实现方式
- Schema-based
1.1 每个通知都需要实现接口或类
1.2 配置spring
配置文件时在<aop:config>
配置 - 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
不能使用&&
由spring
把and
解析成&&
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)
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">
@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 的区别是什么?
- @Compent 作用就相当于 XML配置
- @Bean 需要在配置类中使用,即类上需要加上@Configuration注解
- 两者都可以通过@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 文件
- 在
src
下新建xxx.properties
文件
key=values
- 配置
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
控制事务隔离级别;
事务隔离级别与传播行为
rollback-for=”异常类型全限定路径”
- 当出现什么异常时需要进行回滚
- 建议:给定该属性值.
- 手动抛异常一定要给该属性值.
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。
…