author:空巷
WeChat Applet :Java空巷
QQ: 2399014502
Mail: [email protected]
WeiBo : KongXiang_
WeChat:
推荐学习路径:http://c.biancheng.net/view/4242.html
Spring 被称为 J2EE 的春天,是一个开源的轻量级的 Java 开发框架, 具有控制反转(IoC)和面向切面(AOP)两大核心。Java Spring 框架通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
Spring 框架不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力。
Spring 是另一个主流的 Java Web 开发框架,该框架是一个轻量级的应用框架,具有很高的凝聚力和吸引力。Spring 框架因其强大的功能以及卓越的性能而受到众多开发人员的喜爱。
Spring 是分层的 Java SE/EE full-stack 轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃肿和低效的开发模式。
Spring 具有简单、可测试和松耦合等特点,不仅可以用于服务器端的开发,也可以应用于任何 Java 应用的开发中。Spring 框架的主要优点具体如下。
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
只需要通过配置就可以完成对事务的管理,而无须手动编程。
在实际开发中,通常服务器端采用三层体系架构,分别为表现层(web)、业务逻辑层(service)、持久层(dao)。
Spring 对每一层都提供了技术支持,在表现层提供了与 Struts2 框架的整合,在业务逻辑层可以管理事务和记录日志等,在持久层可以整合 Hibernate 和 JdbcTemplate 等技术。
从设计上看,Spring 框架给予了 Java 程序员更高的自由度,对业界的常见问题也提供了良好的解决方案,因此,在开源社区受到了广泛的欢迎,并且被大部分公司作为 Java 项目开发的首选框架。
Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core Container 和 Test,如图 1 所示。
核心容器(Spring core)
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。BeanFactory使用依赖注入的方式提供给组件依赖。
Spring上下文(Spring context)
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子
邮件、国际化、校验和调度功能。
Spring面向切面编程(Spring AOP)
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使
Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO模块
DAO模式主要目的是将持久层相关问题与一般的的业务规则和工作流隔离开来。Spring 中的DAO提供一致的方式
访问数据库,不管采用何种持久化技术,Spring都提供一直的编程模型。Spring还对不同的持久层技术提供一致的
DAO方式的异常层次结构。
Spring ORM模块
Spring 与所有的主要的ORM映射框架都集成的很好,包括Hibernate、JDO实现、TopLink和IBatis SQL Map等。
Spring为所有的这些框架提供了模板之类的辅助类,达成了一致的编程风格。
Spring Web模块
Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。Web层使用Web层框
架,可选的,可以是Spring自己的MVC框架,或者提供的Web框架,如Struts、Webwork、tapestry和jsf。
Spring MVC框架(Spring WebMVC)
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。Spring
的MVC框架提供清晰的角色划分:控制器、验证器、命令对象、表单对象和模型对象、分发器、处理器映射和视图解析器。Spring支持多种视图技术。
工厂模式分为简单工厂模式,工厂方法模式和抽象工厂模式,它们都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,目的是降低系统中代码耦合度,并且增强了系统的扩展性。
/**
* @author 空巷
* @Date 2020/5/31
*/
public interface Car {
/**
* 汽车运行的方法
*/
void run();
}
/**
* @author 空巷
* @Date 2020/5/31
*/
public class Bmw implements Car {
public void run() {
System.out.println("宝马车跑起来了!!!");
}
}
public class Benz implements Car {
public void run() {
System.out.println("奔驰车跑起来了!!!");
}
}
/**
* @author 空巷
* @Date 2020/5/31
*/
public class CarFactory {
public static Car getCar(String type){
if ("benz".equalsIgnoreCase(type)){
return new Benz();
}else{
return new Bmw();
}
}
}
/**
* @author 空巷
* @Date 2020/5/31
*/
public class Client {
public static void main(String[] args) {
Car bmw = CarFactory.getCar("bmw");
bmw.run();
Car benz = CarFactory.getCar("benz");
benz.run();
}
}
/**
* @author 空巷
* @Date 2020/5/31
*/
public interface Factory {
/**
* 统一创建方法
* @return
*/
Car create();
}
/**
* @author 空巷
* @Date 2020/5/31
*/
public class BenzFactory implements Factory {
public Car create() {
return new Benz();
}
}
public class BmwFactory implements Factory {
public Car create() {
return new Bmw();
}
}
}
/**
* @author 空巷
* @Date 2020/5/31
*/
public class Client {
public static void main(String[] args) {
Factory benzFactory = new BenzFactory();
Car benz = benzFactory.create();
benz.run();
Factory bmwFactory = new BmwFactory();
Car bmw = bmwFactory.create();
bmw.run();
}
}
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。
首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制。
套用好莱坞的一句名言就是:你呆着别动,到时我会找你。
在Spring中实现控制反转的时IOC容器,实现方式是DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。
首先引入依赖(jar)
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.1.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.6.RELEASEversion>
<scope>testscope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.18version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.6.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>utf-8encoding>
configuration>
plugin>
plugins>
build>
写个实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
}
xml文件
<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">
<bean id="userNoArgConstructor" class="com.xinzhi.entity.User"/>
<bean id="userConstructor" class="com.xinzhi.entity.User">
<constructor-arg name="name" value="空巷"/>
<constructor-arg name="age" value="21"/>
bean>
<bean id="userConstructor" class="com.xinzhi.entity.User">
<constructor-arg index="0" value="空巷"/>
<constructor-arg index="1" value="21"/>
bean>
<bean id="userConstructor" class="com.xinzhi.entity.User">
<constructor-arg value="空巷"/>
<constructor-arg value="21"/>
bean>
<bean id="userConstructor" class="com.xinzhi.entity.User">
<constructor-arg type="java.lang.String" value="空巷"/>
<constructor-arg type="java.lang.Integer" value="21"/>
bean>
beans>
来个复杂点的实体类
/**
* @author 空巷
* @Date 2020/5/31
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class User {
private String name;
private int age;
private String gender;
private Address address;
//爱好
private String[] hobbies;
//职务
private List<String> duties;
//家庭关系
private Map<String,String> familyTies;
//购物车商品
private Set<String> carts;
//工作经历
private Properties workExperience;
}
/**
* @author 空巷
* @Date 2020/5/31
*/
@Data
public class Address {
private String addressInfo;
}
配置文件:
<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">
<bean id="userSet" class="com.xinzhi.entity.User">
<property name="name" value="山西"/>
<property name="age" value="21">property>
<property name="hobbies">
<array>
<value>写代码value>
<value>睡觉value>
array>
property>
<property name="duties">
<list>
<value>码侬value>
<value>kongxiangvalue>
list>
property>
<property name="familyTies">
<map>
<entry key="bigSon" value="空巷">entry>
<entry key="smallSon" value="空巷1">entry>
map>
property>
<property name="carts">
<set>
<value>Macvalue>
<value>Phonevalue>
set>
property>
<property name="workExperience">
<props>
<prop key="first">Java工程师prop>
<prop key="second">程序员prop>
props>
property>
bean>
beans>
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring3 为 Bean 定义了五种作用域,具体如下。
singleton 是 Spring 容器默认的作用域,当一个 Bean 的作用域为 singleton 时,Spring 容器中只会存在一个共享的 Bean 实例,并且所有对 Bean 的请求,只要 id 与该 Bean 定义相匹配,就只会返回 Bean 的同一个实例。
在 Spring 配置文件中,可以使用 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:
<bean id="userServiceImpl" class="com.xinzhi.service.UserServiceImpl" scope="singleton"/>
使用 prototype 作用域的 Bean 会在每次请求该 Bean 时都会创建一个新的 Bean 实例。因此对需要保持会话状态的 Bean应该使用 prototype 作用域。
<bean id="account" class="com.xinzhi.entity.User" scope="prototype"/>
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
<bean id="loginAction" class="com.xinzhi.entity.User" scope="request"/>
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
<bean id="userPreferences" class="com.xinzhi.entity.User" scope="session"/>
在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
<bean id="userPreferences" class="com.xinzhi.entity.User" scope="singleton"/>
在 Spring 中,尽管使用 XML 配置文件可以实现 Bean 的装配工作,但如果应用中 Bean 的数量较多,会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。
Java 从 JDK 5.0 以后,提供了 Annotation(注解)功能,Spring 也提供了对 Annotation 技术的全面支持。Spring3 中定义了一系列的 Annotation(注解),常用的注解如下。
1)@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
2)@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
3)@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
4)@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
5)@Autowired
用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。
6)@Resource
其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。
@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。
如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
7)@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class User {
@Value("杨一")
private String name;
private int age;
private String gender;
// @Autowired
// @Qualifier("address2")
@Resource(name = "address2")
private Address address;
}
配置文件:
<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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xinzhi"/>
beans>
除了使用 XML 和 Annotation 的方式装配 Bean 以外,还有一种常用的装配方式——自动装配。自动装配就是指 Spring 容器可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。
要使用自动装配,就需要配置 元素的 autowire 属性。autowire 属性有五个值,具体说明如表 1 所示。
名称 | 说明 |
---|---|
byName | 根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。 |
byType | 根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。 |
constructor | 根据构造方法的参数的数据类型,进行 byType 模式的自动装配。 |
autodetect | 如果发现默认的构造方法,则用 constructor 模式,否则用 byType 模式。 |
no | 默认情况下,不使用自动装配,Bean 依赖必须通过 ref 元素定义 |
加入依赖 配置数据源(上面以加入)
Spring 框架针对数据库开发中的应用提供了 JDBCTemplate 类,该类是 Spring 对 JDBC 支持的核心,它提供了所有对数据库操作功能的支持。
Spring 框架提供的JDBC支持主要由四个包组成,分别是 core(核心包)、object(对象包)、dataSource(数据源包)和 support(支持包),org.springframework.jdbc.core.JdbcTemplate 类就包含在核心包中。作为 Spring JDBC 的核心,JdbcTemplate 类中包含了所有数据库操作的基本方法。
<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="dataSource" class="org.springframework.jdbc.dataSource.DriverManagerDataSource">
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource">
&useUnicode=true&characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="root" />
bean>
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
beans>
生命周期就是一个东西 从 生 到 死 的过程。
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 所示。
看代码吧:
/**
* @author 空巷
* @Date 2020/6/2
*/
public class User implements Serializable {
private int id;
private String name;
private String password;
public User() {
System.out.println("--------构造方法-------");
}
public int getId() {
return id;
}
public void setId(int id) {
System.out.println("----------注入------------");
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void init(){
System.out.println("---------init初始化----------");
}
public void destory(){
System.out.println("-----------destory销毁方法----------");
}
@Override
public String toString() {
System.out.println("-------使用-------");
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
application.xml
<context:component-scan base-package="com.xinzhi"/>
<bean id="user" class="com.xinzhi.entity.User" init-method="init" destroy-method="destory">
<property name="id" value="1"/>
<property name="name" value="zhangsan"/>
<property name="password" value="123456"/>
bean>
测试:
@Test
public void testUser(){
ClassPathXmlApplicationContext cpa =
new ClassPathXmlApplicationContext("application.xml");
User user = cpa.getBean("user", User.class);
System.out.println(user);
cpa.close();
}
结果:
--------构造方法-------
----------注入------------
---------init初始化----------
-------使用-------
User{id=1, name='zhangsan', password='123456'}
-----------destory销毁方法----------
扩展一下:
/**
* @author 空巷
* @Date 2020/6/2
*/
public class BeanHandler implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("----------初始化之前-------------");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("------------初始化之后-------------");
return bean;
}
}
注入Bean
<bean class="com.xinzhi.handler.BeanHandler" />
结果:
--------构造方法-------
----------注入------------
----------初始化之前-------------
---------init初始化----------
------------初始化之后-------------
-------使用-------
User{id=1, name='zhangsan', password='123456'}
-----------destory销毁方法----------
看到结果没?这就是Bean 从生到死的过程。
JDK 动态代理是通过 JDK 中的 java.lang.reflect.Proxy 类实现的。下面通过具体的案例演示 JDK 动态代理的使用。
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多 AOP 框架所使用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字节码并生成新的类。因此 CGLIB 要依赖于 ASM 的包,解压 Spring 的核心包 spring-core-3.2.2.RELEASE.jar
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>2.2.2version>
dependency>
/**
* @author 空巷
* @Date 2020/6/2
*/
public class MaleActor {
private String name;
public MaleActor() {
}
public MaleActor(String name) {
this.name = name;
}
public void work() {
System.out.println(this.name + "开始工作了!");
}
}
@Test
public void testActor(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MaleActor.class);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("****乱来");
Object invoke = method.invoke(new MaleActor("王宝强"), objects);
System.out.println("转移费用");
return invoke;
}
});
MaleActor maleActor = (MaleActor) enhancer.create();
maleActor.work();
}
面向切面编程(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(切面) | 切入点和通知的结合。 |
通俗来讲
就是需要在某些类中,添加很多统一的代码,比如日志,事务,权限等等。
引入依赖
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
spring配置文件中加入头文件
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
/**
* @author 空巷
* @Date 2020/6/2
*/
public interface IUserService {
/**
* 查询方法
*/
void add();
/**
* 删除方法
*/
void delete();
/**
* 更新的方法
*/
void update();
/**
* 查询的方法
*/
void select();
}
/**
* @author 空巷
* @Date 2020/6/2
*/
public class UserServiceImpl implements IUserService {
@Override
public void add() {
System.out.println("这是添加的方法");
}
@Override
public void delete() {
System.out.println("这是删除的方法");
}
@Override
public void update() {
System.out.println("这是更新的方法");
}
@Override
public void select() {
System.out.println("这是查询的方法");
}
}
有两个增强类 , 我们编写两个 , 一个前置增强 一个后置增强
//前置增强类
/**
* @author 空巷
* @Date 2020/6/2
*/
public class LogBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("这是执行" + method.getName() + "方法之前");
}
}
//后置增强类
/**
* @author 空巷
* @Date 2020/6/2
*/
public class LogAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("这是执行" + method.getName() + "方法之后");
}
}
spring配置文件中加入注册
<bean id="before" class="com.xinzhi.log.LogBefore"/>
<bean id="after" class="com.xinzhi.logafter.LogAfter"/>
<bean id="userService" class="com.xinzhi.service.impl.UserServiceImpl"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.xinzhi.service.impl.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pointcut" />
<aop:advisor advice-ref="after" pointcut-ref="pointcut" />
aop:config>
/**
* @author 空巷
* @Date 2020/6/2
*/
public class MyAop {
public void before(){
System.out.println("执行方法之前");
}
public void after(){
System.out.println("执行方法之后");
}
}
xml文件配置
<bean id="myAop" class="com.xinzhi.aop.MyAop"/>
<aop:config>
<aop:aspect ref="myAop">
<aop:pointcut id="pointcut" expression="execution(* com.xinzhi.service.impl.UserServiceImpl.*(..))"/>
<aop:around method="before" pointcut-ref="pointcut"/>
<aop:around method="after" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
测试
@Test
public void testAOP(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
IUserService userService = context.getBean("userService",IUserService.class);
userService.add();
}
/**
* @author 空巷
* @Date 2020/6/2
*/
@Component
@Aspect
public class AnnotationAOP {
@Before("execution(* com.xinzhi.service.impl.*.*(..))")
public void before() {
System.out.println("执行方法之前");
}
@Before("execution(* com.xinzhi.service.impl.*.*(..))")
public void after() {
System.out.println("执行方法之后");
}
@AfterReturning("execution(* com.xinzhi.service.impl.*.*(..))")
public void afterRuning() {
System.out.println("方法返回后afterReturning()");
}
@Around("execution(* com.xinzhi.service.impl.*.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("-------环绕前-------");
System.out.println("签名(拿到方法名):" + jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("-------环绕后------");
System.out.println(proceed);
}
@AfterThrowing("execution(* com.xinzhi.service.impl.*.*(..))")
public void afterThrow() {
System.out.println("--------------有异常发生-----------------" + new Date());
}
}
xml文件配置
<aop:aspectj-autoproxy/>
测试
@Test
public void testAOP(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
IUserService userService = context.getBean("userService",IUserService.class);
userService.add();
}
事务特性分为四个:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持续性
(Durability)简称ACID。
Spring 的事务管理有两种方式:一种是传统的编程式事务管理,即通过编写代码实现的事务管理;另一种是基于 AOP 技术实现的声明式事务管理。由于在实际开发中,编程式事务管理很少使用,所以我们只对 Spring 的声明式事务管理进行详细讲解。
Spring 声明式事务管理在底层采用了 AOP 技术,其最大的优点在于无须通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。
Spring 实现声明式事务管理主要有两种方式:
/**
* @author 空巷
* @Date 2020/6/2
*/
@Mapper
public interface UserMapper {
/**
* 保存用户
* @return
*/
int saveUser(User user);
/**
* 修改用户
* @param user
* @return
*/
int updateUser(User user);
/**
* 新增user
* @param user
* @return
*/
int addUser(User user);
/**
* 获取所有的用户
* @return
*/
List<User> getAllUsers();
}
/**
* @author 空巷
* @Date 2020/6/2
*/
public interface IUserService {
/**
* 测试事务使用
*/
void transaction();
}
/**
* @author 空巷
* @Date 2020/6/2
*/
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
public void transaction() {
userMapper.addUser(new User(33,"zxcasd",22,"男"));
int i = 1/0;
userMapper.addUser(new User(35,"zxcasd",22,"男"));
}
}
配置文件
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.xinzhi.service.impl.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
aop:config>
测试
@Test
public void testTransaction(){
userService.transaction();
}
/**
* @author 空巷
* @Date 2020/6/2
*/
@Service
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
public void transaction() {
userMapper.addUser(new User(33,"zxcasd",22,"男"));
int i = 1/0;
userMapper.addUser(new User(35,"zxcasd",22,"男"));
}
}
配置文件
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
测试
@Test
public void testTransaction(){
userService.transaction();
}
pom.xml文件
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.1.10.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.6.RELEASEversion>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
project>
mybatis-config.xml
<configuration>
<typeAliases>
<package name="com.xinzhi.entity"/>
typeAliases>
configuration>
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sys?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
application.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.0.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.xinzhi"/>
<context:property-placeholder location="db.properties"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xinzhi.dao"/>
bean>
<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>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
bean>
beans>
实体类
/**
* @author 空巷
* @Date 2020/6/2
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private int age;
private String gender;
}
UserMapper
/**
* @author 空巷
* @Date 2020/6/2
*/
@Mapper
public interface UserMapper {
/**
* 保存用户
* @return
*/
int saveUser(User user);
/**
* 修改用户
* @param user
* @return
*/
int updateUser(User user);
/**
* 新增user
* @param user
* @return
*/
int addUser(User user);
/**
* 获取所有的用户
* @return
*/
List<User> getAllUsers();
}
UserMapper.xml
<mapper namespace="com.xinzhi.dao.UserMapper">
<insert id="addUser" parameterType="com.xinzhi.entity.User">
insert into user (id,name,age,gender) values (#{id},#{name},#{age},#{gender})
insert>
<update id="updateUser" parameterType="com.xinzhi.entity.User">
update user set name=#{name},age=#{age} where id = #{id}
update>
<select id="getAllUsers" resultType="com.xinzhi.entity.User">
select id,name,age,gender from user
select>
mapper>