文章目录
1.Spring是什么
- 推荐学习在C语言中文网和spring官网结合学习
Spring 是另一个主流的 Java Web 开发框架,该框架是一个轻量级的应用框架,具有很高的凝聚力和吸引力。Spring 框架因其强大的功能以及卓越的性能而受到众多开发人员的喜爱。
Spring 是分层的 Java SE/EE full-stack 轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃肿和低效的开发模式。
在实际开发中,通常服务器端采用三层体系架构,分别为表现层(web)、业务逻辑层(service)、持久层(dao)。
Spring 对每一层都提供了技术支持,在表现层提供了与 Struts2 框架的整合,在业务逻辑层可以管理事务和记录日志等,在持久层可以整合 Hibernate 和 JdbcTemplate 等技术。
从设计上看,Spring 框架给予了 Java 程序员更高的自由度,对业界的常见问题也提供了良好的解决方案,因此,在开源社区受到了广泛的欢迎,并且被大部分公司作为 Java 项目开发的首选框架。
Spring 具有简单、可测试和松耦合等特点,不仅可以用于服务器端的开发,也可以应用于任何 Java 应用的开发中。Spring 框架的主要优点具体如下。
1.1特点,优势
1)方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
2)方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
3)降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
4)方便程序的测试
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
5)AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
6)声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程。
1.2核心模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WddJ5Yq8-1603873654916)(E:\保存\QQ截图20201020154404.png)]
七大模块(面试高频)
核心容器(Spring Core)
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
应用上下文(Spring Context)
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring面向切面编程(Spring AOP)
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
JDBC和DAO模块(Spring DAO)
JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
对象实体映射(Spring ORM)
Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
Web模块(Spring Web)
Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
MVC模块(Spring Web MVC)
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。
Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core Container 和 Test,如图 1 所示。
图 1 Spring的体系结构
图 1 中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。下面分别对这些模块的作用进行简单介绍。
1. Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
- JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
- ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
- OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
- JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
- Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
2. Web 模块
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
- Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
- Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
- Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
- Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
3. Core Container(核心容器)
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
- Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
- Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
- Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
- Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
4. 其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
- AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
- Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
- Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
- Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
名称 | 作用 |
---|---|
spring-core-3.2.13.RELEASE.jar | 包含 Spring 框架基本的核心工具类,Spring 其他组件都要用到这个包中的类,是其他组件的基本核心。 |
spring-beans-3.2.13.RELEASE.jar | 所有应用都要用到的,它包含访问配置文件、创建和管理 bean 以及进行 Inversion of Control(IoC)或者 Dependency Injection(DI)操作相关的所有类。 |
spring-context-3.2.13.RELEASE.jar | Spring 提供在基础 IoC 功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI 定位、EJB 集成、远程访问、缓存以及各种视图层框架的封装等 |
spring-expression-3.2.13.RELEASE.jar | 定义了 Spring 的表达式语言。 需要注意的是,在使用 Spring 开发时,除了 Spring 自带的 JAR 包以外,还需要一个第三方 JAR 包 commons.logging 处理日志信息 |
2.项目搭建
-
创建maven
-
导入依赖:四个基础包(core,beans,context,expression)(webmvc)
-
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.9.RELEASE</version> </dependency>
-
-
准备好测试类
-
准备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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <!-- 由 Spring容器创建该类的实例对象 --> <bean id="personDao" class="com.mengma.ioc.PersonDaoImpl" /> </beans>
-
class:要被管理的类
-
id:给管理对起的名字,唯一
-
name:同上,但是可以重复,可以用特殊字符
-
-
通过容器获取对象
-
// 定义Spring配置文件的路径 String xmlPath = "applicationContext.xml"; // 初始化Spring容器,加载配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext( xmlPath); // 通过容器获取personDao实例 PersonDao personDao = (PersonDao) applicationContext .getBean("personDao"); // 调用 personDao 的 add ()方法 personDao.add();
-
3.IOC和DI(面试高频)
控制反转,依赖注入
IOC(Inverse of Control)是一种设计思想,以前创建对象的主动权和时机是程序员自己 把控,现在这种权利转移到了spring容器中,由容器根据配置文件创建和管理各个实例之间的依赖关系,对象之间松耦合,利于功能复用。
DI(Dependency Injection)和控制反转是同一个概念的不同角度的描述,即应用程序在运行依赖ioc容器来动态注入对象需要的外部资源。bean之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件bean之中。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,用谁实现。
通过更改对dao层的使用理解控制反转原理
public interface UserDao {
void select();
}
public class UserDaoMysql implements UserDao{
@Override
public void select() {
System.out.println("mysql查询");
}
}
public class UserDaoOracle implements UserDao{
@Override
public void select() {
System.out.println("Oracle");
}
}
public class UserServer {
// UserDao dao=new UserDaoOracle();
UserDao dao;
public UserServer(UserDao dao) {
this.dao = dao;
}
public void find() {
dao.select();
}
public static void main(String[] args) {
UserServer server = new UserServer(new UserDaoMysql());
server.find();
}
}
4.容器
Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext,接下来将针对这两种 IoC 容器进行详细讲解。
4.1BeanFactory
BeanFactory 是基础类型的 IoC 容器,它由 org.springframework.beans.facytory.BeanFactory 接口定义,并提供了完整的 IoC 服务支持。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。
BeanFactory 接口有多个实现类,最常见的是 org.springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配 Bean 的。
创建 BeanFactory 实例时,需要提供 Spring 所管理容器的详细配置信息,这些信息通常采用 XML 文件形式管理。其加载配置信息的代码具体如下所示:
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("D://applicationContext.xml"));
ApplicationContext
ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。该接口的全路径为 org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。
ApplicationContext 接口有两个常用的实现类,具体如下。
1)ClassPathXmlApplicationContext
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
在上述代码中,configLocation 参数用于指定 Spring 配置文件的名称和位置,如 applicationContext.xml。
2)FileSystemXmlApplicationContext
该类从指定的文件系统路径中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“F:/workspaces/applicationContext.xml”。
在使用 Spring 框架时,可以通过实例化其中任何一个类创建 Spring 的 ApplicationContext 容器。
通常在 Java 项目中,会采用通过 ClassPathXmlApplicationContext 类实例化 ApplicationContext 容器的方式,而在 Web 项目中,ApplicationContext 容器的实例化工作会交由 Web 服务器完成。Web 服务器实例化 ApplicationContext 容器通常使用基于 ContextLoaderListener 实现的方式,它只需要在 web.xml 中添加如下代码:
<!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔--><context-param> <param-name>contextConfigLocation</param-name> <!--spring将加载spring目录下的applicationContext.xml文件--> <param-value> classpath:spring/applicationContext.xml </param-value></context-param><!--指定以ContextLoaderListener方式启动Spring容器--><listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class></listener>
需要注意的是,BeanFactory 和 ApplicationContext 都是通过 XML 配置文件加载 Bean 的。
二者的主要区别在于,如果 Bean 的某一个属性没有注入,则使用 BeanFacotry 加载后,在第一次调用 getBean() 方法时会抛出异常,而 ApplicationContext 则在初始化时自检,这样有利于检查所依赖的属性是否注入。
因此,在实际开发中,通常都选择使用 ApplicationContext,而只有在系统资源较少时,才考虑使用 BeanFactory。本教程中使用的就是 ApplicationContext。
4.2创建对象的三种方法
1.空参构造
空参构造就是调用无参构造方法
2.静态工厂(了解)
<bean id="u1" class="xiongxiong.pojo.UserFactory" factory-method="getUser"></bean>
public static User getUser() {
return new User();
}
3.实例工厂(了解)
<bean id="u2" factory-bean="Factory" factory-method="getUser2"></bean>
<bean id="Factory" class="xiongxiong.pojo.UserFactory"></bean>
public User getUser2() {
return new User();
}
5.依赖注入
5.1set方法(重要)
- 基本类型
- 集合,属性
- 数组
- list
- set
- map
- properties
- null
- 引用类型
<bean id="person" class="xiongxiong.pojo.Person">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
<property name="hobby">
<array>
<value>篮球</value>
<value>做爱</value>
</array>
</property>
<property name="girls">
<list>
<value>前台</value>
<value>前台太</value>
</list>
</property>
<property name="boys">
<map>
<entry key="吕龙" value="18"></entry>
<entry key="吕" value="11"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="升高">183</prop>
<prop key="升高">184</prop>
</props>
</property>
<property name="car" ref="car"></property>
</bean>
<bean id="car" class="xiongxiong.pojo.Car">
<property name="name" value="兰博"></property>
<property name="target" value="泡妞"></property>
</bean>
5.2构造方法
- name 实体类对应的名字
- value 当注入的不是依赖对象,而是基本数据类型时,就用value
- index 是索引,指定注入的属性位置,从0开始
- type 是指该属性所对应的类型
- ref 是指引用的依赖对象
public Car(int maxSpeed,String brand, double price){
this.maxSpeed=maxSpeed;
this.brand=brand;
this.price=price;
}
<bean id="car1" class="com.spring.model.Car">
<constructor-arg type="int" value="300"></constructor-arg>
<constructor-arg type="java.lang.String" value="宝马"></constructor-arg>
<constructor-arg type="double" value="300000.00"></constructor-arg>
</bean>
public Car(String brand, String corp,double price){
this.brand=brand;
this.corp=corp;
this.price=price;
}
<!-- 构造函数注入(按索引匹配) -->
<bean id="car2" class="com.spring.model.Car">
<!-- 注意索引从0开始 -->
<constructor-arg index="0" value="宝马"></constructor-arg>
<constructor-arg index="1" value="中国一汽"></constructor-arg>
<constructor-arg index="2" value="300000.00"></constructor-arg>
</bean>
public Car(String brand, String corp,double price){
this.brand=brand;
this.corp=corp;
this.price=price;
}
public Car(String brand, String corp,int maxSpeed){
this.brand=brand;
this.corp=corp;
this.maxSpeed=maxSpeed;
}
<!-- 构造函数注入(通过入参类型和位置索引确定对应关系) -->
<!-- 对应public Car(String brand, String corp,int maxSpeed)构造函数 -->
<bean id="car3" class="com.spring.model.Car">
<constructor-arg index="0" type="java.lang.String" value="奔驰"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="中国一汽"></constructor-arg>
<constructor-arg index="2" type="int" value="200"></constructor-arg>
</bean>
5.3 p命名空间(原理为set)
顶上加上这行代码
xmlns:p="http://www.springframework.org/schema/p"
<bean id="car" class="xiongxiong.pojo.Car" p:name="宝马">
</bean>
5.4 c命名空间(原理为构造)
顶上也是加上这行代码
xmlns:c="http://www.springframework.org/schema/c"
<bean id="car" class="xiongxiong.pojo.Car" c:name="宝马">
</bean>
5.5 sepl(spring expression language)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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="car" class="com.itdjx.spring.spel.Car">
<property name="brand" value="#{'玛莎拉蒂'}"></property>
<property name="price" value="#{32000.78}"></property>
<property name="perimeter"
value="#{T(java.lang.Math).PI * 75.8f}"></property>
</bean>
<bean id="person" class="com.itdjx.spring.spel.Person">
<property name='name' value='#{"华崽儿"}'></property>
<property name="age" value="#{25}"></property>
<property name="marriage"
value="#{car.price > 400000 and age > 30}"></property>
<property name="car" value="#{car}"></property>
<property name="socialStatus"
value="#{car.price > 30000 ? '金领' : '白领'}"></property>
<property name="address"
value="#{address.province + '省' + address.city + '市' + address.area + '区'}" />
</bean>
<bean id="address" class="com.itdjx.spring.spel.Address">
<property name="province" value="#{'辽宁'}" />
<property name="city" value="#{'大连'}" />
<property name="area" value="#{'沙河口'}" />
</bean>
</beans>
6.bean作用域和模块化
6.1 scope属性
1)singleton
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
2)prototype
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
3)request
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
4)session
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
5)global Session
在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
6)application和 global Session一样
Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
大概意思是我的项目开着就一直有效
7)websocket
Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
大概意思和Tomcat一样只要服务器一直开着就一直有效
单例模式案例:
public class TestTTTT {
static TestTTTT test1=null;
public static TestTTTT geTttt() {
if (test1 == null) {
test1=new TestTTTT();
}
return test1;
}
public static void main(String[] args) {
System.out.println(TestTTTT.geTttt());
System.out.println(TestTTTT.geTttt());
System.out.println(TestTTTT.geTttt());
}
}
6.2初始化和销毁
-
init-method
-
destroy-method
-
销毁用这类 他是ApplicationContext 的子类
-
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(xmlPath); context.close();
-
-
6.3模块化配置
- import resource=""
- 导入其他的xml文档
7.生命周期
Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。
而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。
了解 Spring 生命周期的意义就在于,可以利用 Bean 在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,会在 Bean 被初始化后和被销毁前执行一些相关操作。
在 Spring 中,Bean 的生命周期是一个很复杂的执行过程,我们可以利用 Spring 提供的方法定制 Bean 的创建过程。
当一个 Bean 被加载到 Spring 容器时,它就具有了生命,而 Spring 容器在保证一个 Bean 能够使用之前,会进行很多工作。Spring 容器中 Bean 的生命周期流程如图 1 所示。
图 1 Bean 的生命周期
Bean 生命周期的整个执行过程描述如下。
1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。
2)利用依赖注入完成 Bean 中所有属性值的配置注入。
3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
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 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为 scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
Spring 为 Bean 提供了细致全面的生命周期过程,通过实现特定的接口或 的属性设置,都可以对 Bean 的生命周期过程产生影响。虽然可以随意配置 的属性,但是建议不要过多地使用 Bean 实现接口,因为这样会导致代码和 Spring 的聚合过于紧密。
8.自动装配和注解开发
8.1自动装配
spring会在上下文中自动寻找,自动给bean装配属性
三种方式
8.1.1在xml中显示的配置
<bean id="user" class="xiongxiong.pojo.User" />
8.1.2隐式的自动装配(重要)
-
autowire属性自动装配
- byName
- 根据名字找
- byType
- 根据类型找
- byName
-
注解自动装配
-
导入约束(context)
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
-
配置注解支持
<context:annotation-config />
@Autowired
用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。
@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
@Resource
其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。
@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。
如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
@Resource(name="",type=Car.class)
注意: eclipes jdk版本问题要在1.8以上才有
-
8.1.3在java中显示配置
@Configuration
作用,该类作为配置,不打也可以
打上的话,你配置的获得类对象默认是单列,不配置,就不是单列
@Bean
8.2注解开发
spring4之后,使用注解开发,需要导入aop包
jdk1.5支持注解,spring2.5支持注解
步骤
-
导入aop包
-
添加context约束
-
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
-
-
扫描指定包
-
<context:component-scan base-package="扫包名"/>
@Component
组件,把该类交给spring托管
@Repository
数据库
@Controller
控制层
@Service
服务层
@Value
赋值
@Scope
作用域
@PostConstruct
初始化数据
@PreDestroy
销毁数据
-
比较
- 优势:注解开发简化代码,方便
- 缺点:复杂类型注入不方便,不便于维护.
9.整合单元测试
-
添加Junit依赖
-
添加spring-test依赖
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.9.RELEASE</version> </dependency>
-
-
@RunWith(SpringJUnit4ClassRunner.class)//通过junt4启动容器类 @ContextConfiguration("classpath:applicationContext.xml")//通过类路径加载xml文件 public class SPTest { @Autowired User user; @Test public void fun1() { System.out.println(user); }
反射基础
类加载
- 类加载指的是将类的class文件读入内存,并为创建一个java.lang.Class对象
- 当一个变量被修饰成 static final,并在编译器就能确定其值,也就是常量,那么通过类名,变量名访问该变量时不会加载该类(也就是访问常量并不会初始化这个类)
类初始化时机
类从加载到虚拟机内存开始,到卸载出内存为止,真个生命周期包括:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)七个阶段。其中,验证、准备、解析三个部分统称为连接(Linking)
-
new对象,调用类的静态方法或静态变量时
-
虚拟机启动时,加载用户指定的main方法的所在类
-
通过反射调用某个类
-
初始化一个类时先初始化它的父类
-
初始化某个类的子类
中心思想:初始化时机为该类首次主动使用
以下情况不会初始化1、通过子类类名调用父类静态代码,不会触发子类的初始化。
2、通过数组来创建对象不会触发此类的初始化。
3、通过调用静态常量不会触发初始化。
类加载器
-
Bootstrap ClassLoader,被称为引导(也称为原始或根)类加载器。它负责加载 Java的核心类。在Sun的JVM中,当执行java.exe的命令时使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值可以指定加载附加的类。根类加载器非常特殊,它并不是java.lang.ClassLoader的子类,而是由JVM自身实现的。
-
Extension ClassLoader,被称为扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或由java.ext.dirs系统属性指定的目录)中的JAR的类包。
-
System ClassLoaser,被称为系统(也称为应用)类加载器,它负责在JVM启动时,加载来自命令java中的-classpath选项或java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。
反射
概念
官方解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简单理解
:反射就是直接通过字节码文件class,去使用它的变量,方法,和构造
使用步骤
- 获得class对象
- 从class中获取信息
点击链接看
https://blog.csdn.net/wuzhangxiong123/article/details/108062608
或者这个看https://www.cnblogs.com/duibd/p/12060964.html
结合配置文件的使用
-
reflect.properties文件
-
在文件里面写class和要执行的方法
-
然后添加测试类
-
className=xiongxiong.pojo.Aaaa//执行包下的类 methodName=setName//执行该类下的方法
-
import java.io.FileInputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; import org.junit.Test; public class ClassTest { @Test public void te() throws Exception { //读取配置文件 Properties properties=new Properties(); FileInputStream inputStream=new FileInputStream("reflect.properties"); properties.load(inputStream);//load加载该执行文件 String className = properties.getProperty("className"); String methodName = properties.getProperty("methodName"); //反射 Class clzss = Class.forName(className); Constructor constructor = clzss.getConstructor(); Object object = constructor.newInstance(); Method method = clzss.getMethod(methodName, String.class); method.invoke(object, "tom"); System.out.println(object); } }
-
操作数组
可以通过java.lang.reflect下的Array类来操作数组
- static Object new Instance(Class<?> componentType,int… length):创建一个具有指定的元素类型、指定维度的数组
- static xxx get(Object array,int index) 获取数据
- static void set(Object array,in index,xxx value)修改数组
代理模式
**代理模式的定义:**代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:
为什么要用代理模式?
- **中介隔离作用:**在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
- **开闭原则,增加功能:**代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类
静态代理和动态代理
静态代理
- 不带接口 也可以 但是不规范 因为java是单继承 所以要使用接口
- 带接口 通过代理对象来操作目标对象,可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。 要求是代理类和目标类都得实现相同的接口
常规的代理模式有以下三个部分组成:
- 功能接口
interface IFunction {
void doAThing();
}
复制代码
- 功能提供者(接口的实现类)
class FunctionProvider implement IFunction {
@Override
public void doAThing {
System.out.print("do A");
}
}
复制代码
- 功能代理者
public class Proxy implements IFunction {
private IFunction provider;
Proxy(IFunction provider) {
this.provider = provider;
}
@Override
public void doAThing() {
//在这里可以添加自己需要给每个功能提供者都代理上的公用逻辑
doSomeThing();
provider.doAThing();
}
private void doSomeThing() {
System.out.println("This Time:" + (System.currentTimeMillis()));
}
}
复制代码
运行一下看最终结果
public class MyClass {
public static void main(String args[]) {
IFunction iFunction = new FunctionProvider(); //新建具体实现类
Proxy proxy = new Proxy(iFunction); //放入代理类
proxy.doAThing(); //执行最终的逻辑,给方法套用相同的逻辑
}
}
优点缺点
可以做到在不修改目标对象的功能前提下,对目标功能扩展.
代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
动态代理
- 通过Proxy创建动态代理对象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) //三个参数分别为目标对象的类加载器,目标对象所有接口,实现了InvocationHandler接口的类
-
InvocationHandler接口,重写invoke方法
-
public interface Student { public void write(); public void copy(); } public class StudentImpl implements Student{ @Override public void write() { System.out.println("自己写作业"); } @Override public void copy() { System.out.println("抄作业"); } } public class Home implements InvocationHandler{ private Object target; public Home(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("打开作业"); Object object = method.invoke(target);//Object[] args 这里的args反射里的参数 System.out.println("合上作业"); return object; } } public class Proxyfactory { public static Object getProxy(Object target) { Home home=new Home(target); Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),home); return object; } } public class Test { public static void main(String[] args) { Student target=new StudentImpl(); Student student= (Student) Proxyfactory.getProxy(target); student.copy(); student.write(); } }
-
作用:
- 解决多个方法中调用了一个通用功能的问题
- 解耦
调用代理对象的方法时,其实走的就是重写的invoke方法,invoke方法里我们去调用了共用的方法,调用对象本身方法时就用到了反射
思考:为什么jdk动态代理,被代理类一定要实现接口?
- 生成的代理类默认继承了proxy类,所以可以可以做一些底层操作,但是因为java单继承,所以这个代理对象没办法获得被代理对象的方法,这时代理对象就必须要通过实现接口的方式来获得被代理对象的方法
- 举例:美团跑腿
aop概念
面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。
AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。
Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。
AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。
为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含 Joinpoint、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示。
名称 | 说明 |
---|---|
Joinpoint(连接点) | 指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。 |
Pointcut(切入点) | 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 |
Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 |
Target(目标) | 指代理的目标对象。 |
Weaving(植入) | 指把增强代码应用到目标上,生成代理对象的过程。 |
Proxy(代理) | 指生成的代理对象。 |
Aspect(切面) | 切入点和通知的结合。 |
记住:横向重复,纵向抽取
动态代理的方法
-
原生开发-接口-基于jdk
-
JDK 动态代理是通过 JDK 中的 java.lang.reflect.Proxy 类实现的。下面通过具体的案例演示 JDK 动态代理的使用。
1. 创建项目
在 MyEclipse 中创建一个名称为 springDemo03 的 Web 项目,将 Spring 支持和依赖的 JAR 包复制到 Web 项目的 WEB-INF/lib 目录中,并发布到类路径下。
2. 创建接口 CustomerDao
在项目的 src 目录下创建一个名为 com.mengma.dao 的包,在该包下创建一个 CustomerDao 接口,编辑后如下所示。
package com.mengma.dao;public interface CustomerDao { public void add(); // 添加 public void update(); // 修改 public void delete(); // 删除 public void find(); // 查询}
3. 创建实现类 CustomerDaoImpl
在 com.mengma.dao 包下创建 CustomerDao 接口的实现类 CustomerDaoImpl,并实现该接口中的所有方法,如下所示。
package com.mengma.dao;public class CustomerDaoImpl implements CustomerDao { @Override public void add() { System.out.println("添加客户..."); } @Override public void update() { System.out.println("修改客户..."); } @Override public void delete() { System.out.println("删除客户..."); } @Override public void find() { System.out.println("修改客户..."); }}
4. 创建切面类 MyAspect
在 src 目录下,创建一个名为 com.mengma.jdk 的包,在该包下创建一个切面类 MyAspect,编辑后如下所示。
package com.mengma.jdk;public class MyAspect { public void myBefore() { System.out.println("方法执行之前"); } public void myAfter() { System.out.println("方法执行之后"); }}
上述代码中,在切面中定义了两个增强的方法,分别为 myBefore() 方法和 myAfter() 方法,用于对目标类(CustomerDaoImpl)进行增强。
5. 创建代理类 MyBeanFactory
在 com.mengma.jdk 包下创建一个名为 MyBeanFactory 的类,在该类中使用 java.lang.reflect.Proxy 实现 JDK 动态代理,如下所示。
package com.mengma.jdk;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import com.mengma.dao.CustomerDao;import com.mengma.dao.CustomerDaoImpl;public class MyBeanFactory { public static CustomerDao getBean() { // 准备目标类 final CustomerDao customerDao = new CustomerDaoImpl(); // 创建切面类实例 final MyAspect myAspect = new MyAspect(); // 使用代理类,进行增强 return (CustomerDao) Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), new Class[] { CustomerDao.class }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { myAspect.myBefore(); // 前增强 Object obj = method.invoke(customerDao, args); myAspect.myAfter(); // 后增强 return obj; } }); }}
上述代码中,定义了一个静态的 getBean() 方法,这里模拟 Spring 框架的 IoC 思想,通过调用 getBean() 方法创建实例,第 14 行代码创建了 customerDao 实例。
第 16 行代码创建的切面类实例用于调用切面类中相应的方法;第 18~26 行就是使用代理类对创建的实例 customerDao 中的方法进行增强的代码,其中 Proxy 的 newProxyInstance() 方法的第一个参数是当前类的类加载器,第二参数是所创建实例的实现类的接口,第三个参数就是需要增强的方法。
在目标类方法执行的前后,分别执行切面类中的 myBefore() 方法和 myAfter() 方法。
6. 创建测试类 JDKProxyTest
在 com.mengma.jdk 包下创建一个名为 JDKProxyTest 的测试类,如下所示。
package com.mengma.jdk;import org.junit.Test;import com.mengma.dao.CustomerDao;public class JDKProxyTest { @Test public void test() { // 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象) CustomerDao customerDao = MyBeanFactory.getBean(); // 执行方法 customerDao.add(); customerDao.update(); customerDao.delete(); customerDao.find(); }}
上述代码中,在调用 getBean() 方法时,获取的是 CustomerDao 类的代理对象,然后调用了该对象中的方法。
7. 运行项目并查看结果
使用 JUnit 测试运行 test() 方法,运行成功后,控制台的输出结果如图 1 所示。
从图 1 的输出结果中可以看出,在调用目标类的方法前后,成功调用了增强的代码,由此说明,JDK 动态代理已经实现。
-
-
cglib-类
-
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多 AOP 框架所使用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字节码并生成新的类。因此 CGLIB 要依赖于 ASM 的包,解压 Spring 的核心包 spring-core-3.2.2.RELEASE.jar,文件目录如图 1 所示。
图 1 spring-core-3.2.2.RELEASE.jar文件在图 1 中可以看出,解压的核心包中包含 cglib 和 asm,也就是说 Spring3.2.13 版本的核心包已经集成了 CGLIB 所需要的包,所以在开发中不需要另外导入 ASM 的 JAR 包了。下面通过案例演示实现 CGLIB 的代理过程。
1. 创建目标类 GoodsDao
在 com.mengma.dao 包下创建目标类 GoodsDao,在类中定义增、删、改、查方法,并在每个方法编写输出语句,如下所示。
package com.mengma.dao;public class GoodsDao { public void add() { System.out.println("添加商品..."); } public void update() { System.out.println("修改商品..."); } public void delete() { System.out.println("删除商品..."); } public void find() { System.out.println("修改商品..."); }}
2. 创建代理类 MyBeanFactory
在 src 目录下创建一个名为 com.mengma.cglib 的包,该包下创建类 MyBeanFactory,如下所示。
package com.mengma.cglib;import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import com.mengma.dao.GoodsDao;import com.mengma.jdk.MyAspect;public class MyBeanFactory { public static GoodsDao getBean() { // 准备目标类 final GoodsDao goodsDao = new GoodsDao(); // 创建切面类实例 final MyAspect myAspect = new MyAspect(); // 生成代理类,CGLIB在运行时,生成指定对象的子类,增强 Enhancer enhancer = new Enhancer(); // 确定需要增强的类 enhancer.setSuperclass(goodsDao.getClass()); // 添加回调函数 enhancer.setCallback(new MethodInterceptor() { // intercept 相当于 jdk invoke,前三个参数与 jdk invoke—致 @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { myAspect.myBefore(); // 前增强 Object obj = method.invoke(goodsDao, args); // 目标方法执行 myAspect.myAfter(); // 后增强 return obj; } }); // 创建代理类 GoodsDao goodsDaoProxy = (GoodsDao) enhancer.create(); return goodsDaoProxy; }}
上述代码中,应用了 CGLIB 的核心类 Enhancer。在第 19 行代码调用了 Enhancer 类的 setSuperclass() 方法,确定目标对象。
第 21 行代码调用 setCallback() 方法添加回调函数;第 24 行代码的 intercept() 方法相当于 JDK 动态代理方式中的 invoke() 方法,该方法会在目标方法执行的前后,对切面类中的方法进行增强;第 33~34 行代码调用 Enhancer 类的 create() 方法创建代理类,最后将代理类返回。
3. 创建测试类
在 com.mengma.cglib 包下创建测试类 CGLIBProxyTest,编辑后如下所示。
package com.mengma.cglib;import org.junit.Test;import com.mengma.dao.GoodsDao;public class CGLIBProxyTest { @Test public void test() { // 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象) GoodsDao goodsDao = MyBeanFactory.getBean(); // 执行方法 goodsDao.add(); goodsDao.update(); goodsDao.delete(); goodsDao.find(); }}
上述代码中,调用 getBean() 方法时,依然获取的是 goodsDao 的代理对象,然后调用该对象的方法。使用 JUnit 测试运行 test() 方法,运行成功后
-
-
javasist-class (知道一下就可以)
– springapi 是继承了前置通知接口
-
package com.mengma.factorybean; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; //需要实现接口,确定哪个通知,及告诉Spring应该执行哪个方法 public class MyAspect implements MethodInterceptor { public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("方法执行之前"); // 执行目标方法 Object obj = mi.proceed(); System.out.println("方法执行之后"); return obj; } }
-
aspectj(重要)
- xml
- 注解
aspectj具体实现
导入依赖:需要aop包和aspectj,同时添加aop约束
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
aop
横向重复代码,纵向抽取
aop名词
- joinpoint 连接点:目标对象中,所有可以增强的方法(自己的功能)
- pointcut 切入点:目标对象,需要被拦截的方法,也就是从哪个连接点开始增强
- advice 通知/增强:增强的代码,(共用代码)
- target 目标对象:被代理对象
- weaving 织入:把通知应用到连接点的过程
- proxy 代理:把通知织入到目标对象后形成的代理
- aspect 切面:切入点+通知,是一个类,即已经被增强的类
导包
- aop
- aspects
- aopalliance
- weaver
实现aop(高频)
有两种实现方式,一种是spring aop,几乎是原生开发,比较麻烦。
一种是AspectJ开发,这是一个aop框架,开发比较常用,下面是实现
- 原生开发
- cjlib
- springaop
- aspectj
通知类型
- 前置通知
- 后置通知(如果出现异常不会调用)
- 环绕通知
- 异常拦截通知
- 后置通知(无论是否有异常都会调用)
步骤
通过xml
- 配置目标对象
- 配置通知对象
- 织入:指定切入点,指定通知类,指定需要织入的方法
切入点配置:
public void com.xxx.yyy.UserServiceImpl.save()
void com.xxx.yyy.UserServiceImpl.save()
* com.xxx.yyy.UserServiceImpl.save()
* com.xxx.yyy.UserServiceImpl.*()
* com.xxx.yyy.*Impl.*(..) //..表示任何参数列表都可以
* com.xxx.yyy..*Impl.*(..) //可以连带子包一起查询 注意*后面有空格!!!
- 测试
通过注解的方式aop
- 配置文件里用
<aop:aspectj-autoproxy />
- 通知类开头添加@Aspect注解,通知方法前加注解,括号里为连接点
注:每个通知前配置切入点,后期不方便修改和维护,可以用@Pointcut注解来解决,注解下面创建一个空方法。
通知前调用类名.空方法名,即可找到切入点
注意点:
xml约束一定要弄对 数据库要是打上这@Repository(value=“dao”) dao是别名
@Resource(name=“dao”)
UserDao userdaomysql; 因为是接口开发使用要使用接口对象
spring整合mybatis
什么是 MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession
并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException
。最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
动机
Spring 2.0 只支持 iBatis 2.0。那么,我们就想将 MyBatis3 的支持添加到 Spring 3.0 中(参见 Spring Jira 中的问题)。不幸的是,Spring 3.0 的开发在 MyBatis 3.0 官方发布前就结束了。由于 Spring 开发团队不想发布一个基于未发布版的 MyBatis 的整合支持,如果要获得 Spring 官方的支持,只能等待下一次的发布了。基于在 Spring 中对 MyBatis 提供支持的兴趣,MyBatis 社区认为,应该开始召集有兴趣参与其中的贡献者们,将对 Spring 的集成作为 MyBatis 的一个社区子项目。
知识基础
在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要——因为本手册中不会提供二者的基本内容,安装和配置教程。
MyBatis-Spring 需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | Java |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
整合是使用spring的数据库
<!--这里使用jdbc方式连接,所以导入jdbc包。如果需要用连接池,就导入对应连接池的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
准备mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 设置别名 -->
<typeAliases>
<!-- 2. 指定扫描包,会把包内所有的类都设置别名,别名的名称就是类名,大小写不敏感 -->
<package name="com.mybatis.pojo" />
</typeAliases>
</configuration>
准备pojo,mapper.xml,mapper.java
准备数据库源文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xsh?characterEncoding=utf8
jdbc.username=root
jdbc.password=root123
准备配置spring配置文件
<!-- 加载数据库配置项 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 配置数据源,这里用的是jdbc的方式。数据源可以替换为任意的数据库连接池,只需要导入对应的jar 包即可 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 配置mybatis核心配置文件 -->
<property name="configLocation" value="classpath:mybatis.xml" /> <!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:com/xz/mapper/*.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- 上面扫描mapper只能扫描xml文件,如果想扫描java文件,用下面的写法 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xz.mapper" />
</bean>
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="mapperInterface" value="com.xz.mapper.UserMapper" />
</bean>
第一种测试
第二种测试
@Autowired
ClassMapper mapper;
@Test
public void test11() {
ClassTable classTable = (ClassTable)mapper.findByID(5);
System.out.println(classTable);
}
spring日志
日志在哪里都可以用 我总结到spring里面了 只要依赖和log4j.properties配置也加进去编译器就会自己读取
先到到依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.properties配置 这个是自己配的
### set log levels ###
log4j.rootLogger = debug , stdout , D , E
### Êä³öµ½¿ØÖÆ̨ ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
### Êä³öµ½ÈÕÖ¾Îļþ ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ./logs/log111.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### ±£´æÒì³£ÐÅÏ¢µ½µ¥¶ÀÎļþ ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
网上找的
#####################################
# log4j配置相关说明
######################################
#%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
#%r 输出自应用启动到输出该log信息耗费的毫秒数
#%c 输出所属的类目,通常就是所在类的全名
#%t 输出产生该日志事件的线程名
#%m 输出代码中指定的信息
#%n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
#%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MM dd HH:mm:ss,SSS},输出类似: 2002年10月18日 22:10:28,921
#%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
#log4j提供4种布局:
#org.apache.log4j.HTMLLayout(以HTML表格形式布局)
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息
#log4j中有五级logger 输出级别:
#FATAL 0
#ERROR 3
#WARN 4
#INFO 6
#DEBUG 7
######################################
# log4j相关配置
######################################
#日志输出级别
log4j.rootLogger=INFO,stdout,other
#设置stdout的日志输出控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#输出日志到控制台的方式,默认为System.out
log4j.appender.stdout.Target = System.out
#设置使用灵活布局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#灵活定义输出格式
log4j.appender.stdout.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}] %l %m %n
#设置other的日志输出控制台
log4j.appender.other=org.apache.log4j.RollingFileAppender
#设置other的输出日志
log4j.appender.other.File=${webapp.root}/WEB-INF/logs/log.log
#设置other的日志最大限制
log4j.appender.other.MaxFileSize=1024KB
#最多只保存20个备份文件
log4j.appender.other.MaxBackupIndex=1000
#输出INFO级别以上的日志
og4j.appender.other.Threshold=INFO
#设置使用灵活布局
log4j.appender.other.layout=org.apache.log4j.PatternLayout
#灵活定义输出格式
log4j.appender.other.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}] %l %t %m %n
######################################
# hibernate相关配置
######################################
#输出hibernate调试过程中的错误日志
log4j.logger.org.hibernate=other
#输出HQL查询调试日志
log4j.logger.org.hibernate.hql.ast.AST=other
#输出SQL语句调试日志
log4j.logger.org.hibernate.SQL=other
#输出 JDBC参数查询的日志
log4j.logger.org.hibernate.type=other
#输出缓存日志
log4j.logger.org.hibernate.cache=other
#输出事务日志
log4j.logger.org.hibernate.transaction=other
#输出获取JDBC资源日志
log4j.logger.org.hibernate.jdbc=other
简单测试
Logger looger=Logger.getLogger(Testaa.class);//先弄出 Logger.getLogger 然后在Logger.getLogger(xxx.class) 这个是你本类的class文件
@Autowired
ClassMapper mapper;
@Test
public void test11() {
looger.debug("xxx开始");
looger.debug("xxx查询");
ClassTable classTable = (ClassTable)mapper.findByID(5);
System.out.println(classTable);
looger.debug("xxx结束");
}
spring事务
- 编码式(了解) 已经不使用了
- 声明式
tx包和约束
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager
对象:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
配置事务通知
<!-- 配置事务的增强 -->
<tx:advice id="txAdvice"
transaction-manager="transactionManager">
<tx:attributes>
<!-- isolation="DEFAULT" 隔离级别
propagation="REQUIRED" 传播行为
read-only="false" 只读
timeout="-1" 过期时间
rollback-for="" -Exception
no-rollback-for="" +Exception
-->
<tx:method name="transfer" propagation="REQUIRED" /><!--transfer 的名字 -->
</tx:attributes>
</tx:advice>
事务传播行为
在声明式的事务处理中,要配置一个切面, 其中就用到了propagation,表示打算对这些方法怎么使用事务,是用还是不用,其中propagation有七种配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默认是REQUIRED。
二、 Spring中七种Propagation类的事务属性详解:
**REQUIRED:**支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
三、注意.
这个配置将影响数据存储,必须根据情况选择
配置事务的切面
<aop:config>
<aop:pointcut id="pc" expression="execution(*.com.xz.……)" /><!--server层的类-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />
</aop:config>
事务注解开发
@Transactional
<!-- 配置注解事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
//@Transactional 注解可以声明在类上,也可以声明在方法上。在大多数情况下,方法上的事务会首先执行
@Transactional(readOnly = true)
public class HelloService{
public Foo getFoo(String fooName) {
}
//@Transactional 注解的事务设置将优先于类级别注解的事务设置 propagation:可选的传播性设置
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Hel hel) {
}
}
.@Transactional属性
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
2.事物传播行为
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
3.事务隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
4.事物超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制
超时设置
@Transactional(timeout=30) //默认是30秒
5.事务只读属性
只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。
spring事务回滚规则
指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。
高级(面试)
循环依赖
- spring当中默认单列支持循环
- https://juejin.im/post/6844903955072286727 自己觉得还行 点开看看
整合的全部依赖
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!--这里使用jdbc方式连接,所以导入jdbc包。如果需要用连接池,就导入对应连接池的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
mybayis.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 环境配置标签 -->
<!-- <settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings> -->
<typeAliases>
<package name="xiongmao.qn.pojo"/>
</typeAliases>
<!--将mapper文件加入到配置文件中 -->
<mappers>
<!-- <mapper resource="Test.xml"/> -->
<package name="xiongmao.qn.mapper"/>
</mappers>
</configuration>
spring.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<context:component-scan base-package="xiongmao.qn.pojo"></context:component-scan>
<context:annotation-config />
<!-- 加载数据库配置项 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 配置数据源,这里用的是jdbc的方式。数据源可以替换为任意的数据库连接池,只需要导入对应的jar 包即可 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置mybatis核心配置文件 -->
<!-- <property name="configLocation" value="classpath:mybatis.xml" /> -->
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:xiongmao/qn/mapper/*.xml" />
</bean>
<!-- <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean> -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="xiongmao.qn.mapper" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的增强 -->
<tx:advice id="txAdvice"
transaction-manager="transactionManager">
<tx:attributes>
<!-- isolation="DEFAULT" 隔离级别
propagation="REQUIRED" 传播行为
read-only="false" 只读
timeout="-1" 过期时间
rollback-for="" -Exception
no-rollback-for="" +Exception
-->
<tx:method name="transfer" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
</beans>