1.Spring框架的介绍
1.1 传统的项目的架构
在传统的项目中,一般遵循MVC开发模型。
(1) view层与用户进行交互,显示数据或者将数据传输给view层。
(2) 在controller层创建service层对象,调用service层中业务方法。
(3) 在service层创建dao层对象,调用dao层中操作数据的方法。
(4) dao层进行具体的数据库操作
1.2 传统的项目架构缺陷
程序在一定程度上存在耦合性,不利于程序的扩展。在controller中直接创建了service层类的对象。如果service的业务发生了变更,那么就需要修改controller层的代码。
在进行程序的扩展时,不建议在当前程序的基础上直接修改程序,为了保证之前程序的正常,一般遵循开闭原则进行维护性的修改,对扩展开放,对修改关闭。
例如:用户注册功能。用户注册,从账号、密码、手机号等信息即可。随着行业发展,目前要求进行实名认证。
1.已注册未认证用户登录时进行认证。
2.新用户注册后要求进行认证。
为避免对已有业务的改动,可以新建一个service类,重写注册方法。则在controller层需要创建新的service对象。每个相关的controller层的代码都需要改动,并且每个controller都需要创建对象,存在对象的浪费。
面向过程—>面向对象---->面向接口---->面向切面(组件)
ArrayList aList = new ArrayList();
List aList = new LinkedList()
1.3 解决方案
基于每个controller都要修改service的创建问题,可以为service定义一个统一的创建方式,例如对象工厂模式,使用工厂创建对象,这样以后使用工厂创建对象的对象需要维护时,只需要修改对象工厂即可,且可以结合单例模式,对象工厂返回单例,这样优化了对象重复的浪费问题。
虽然单例工厂能够解决对象的维护和重复问题。但是,随着service层的扩大,工厂也逐渐臃肿,基本每个service会对应一个factory。基于这种情况,则又需要解决工厂臃肿的问题,为此可以利用反射技术,反射可以创建任意类的对象。但是,工厂为保证单例,只能存储的对象只有一个,而controller层需要使用不同的service层对象,为保证对象的有效,且反射性能相对较低,基于这样的情况,则可以定义一个需要创建的对象的清单和一个存储对象的容器,根据清单创建对象,然后将所有创建service对象进行存储,每个controller只需要去容器中获取对象即可,这样既解决了单例问题,也提高了性能。
1.3.1 代码示例
1.3.2 对象清单
1.3.3 对象容器工厂
package org.springframework.context;public interface ApplicationContext {/*** 获取spring容器创建好的对象* @param name* @return*/Object getBean(String name);/*** 获取spring容器创建好的对象* @param name* @return*/<T> T getBean(String name,Class<T> c);}package org.springframework.context.support;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import org.springframework.context.ApplicationContext;import java.io.InputStream;import java.util.HashMap;import java.util.List;import java.util.Map;/*** spring的核心类*/public class ClassPathXmlApplicationContext implements ApplicationContext {/*** 用于存放id或者name与某个类对象的映射关系*/private Map<String,Object> objMap = new HashMap<>();public ClassPathXmlApplicationContext(){}public ClassPathXmlApplicationContext(String xmlPath){try {//将spring-context.xml中配置的bean对应的类的对象默认全部创建好SAXReader reader = new SAXReader();InputStream ins = ClassPathXmlApplicationContext.class.getClassLoader().getResourceAsStream(xmlPath);Document doc = reader.read(ins);//读取xml的根标签Element rootElement = doc.getRootElement();//拿到beans根标签下的所有bean子标签List<Element> childEleList = rootElement.elements();for(Element temp : childEleList){//读取某个bean标签的id、name、class的属性值String id = temp.attributeValue("id");String name = temp.attributeValue("name");String cls = temp.attributeValue("class");Object obj = Class.forName(cls).newInstance();//以id做key,拿到class的全路径创建对象作为value//将键值对存储到objMap中objMap.put(id,obj);//拿到name以,切割 以切割多个字符串做key,拿到class的全路径创建对象作为valueString[] nameAtt = name.split(",");for(String strName: nameAtt){objMap.put(strName,obj);}}} catch (Exception e) {e.printStackTrace();}}/*** 获取spring容器创建好的对象* @param name* @return*/@Overridepublic Object getBean(String name) {return objMap.get(name);}/*** 获取spring容器创建好的对象* @param name* @return*/@Overridepublic <T> T getBean(String name, Class<T> c) {return (T)objMap.get(name);}}
1.3.4 程序相关类
1.3.4.1 test
package com.powernode.test;import com.powernode.domain.Dog;import com.powernode.domain.User;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SelfSpringTest {public static void main(String[] args) {//启动spring容器ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");User u1 = context.getBean("u1", User.class);u1.sleep();}}
2.Spring的介绍
2.1 简介
Spring框架是由于软件开发的复杂性而创建的,初衷是为了让软件开发更加简单。Spring使用的是简单的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Web Service,有2个显著特点:
1.数据格式是xml格式。
2.配置繁琐,“笨重”,对象关联性大,需在配置文件中各种配置。
基于这些原因,Spring框架提出了:IOC/DI(控制反转/依赖注入),AOP(面向切面编程)。
Spring框架可在任何类型的部署平台上为基于Java的现代企业应用程序提供全面的编程和配置模型。Spring的关键元素是在应用程序级别的基础架构支持:Spring专注于企业应用程序的“管道”,以便团队可以专注于应用程序级别的业务逻辑,而不必与特定的部署环境建立不必要的联系。
2.2 Spring的核心组件
2.2.1 核心容器
核心容器由 spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
l spring-core 模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
l spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
l context 模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。Context 模块继承自 Bean 模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过 Servelet 容器)等功能。Context 模块也支持 Java EE 的功能,比如 EJB、JMX 和远程调用等。ApplicationContext 接口是 Context 模块的焦点。spring-context-support 提供了对第三方库集成到 Spring 上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
l spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是 JSP2.1 规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从 Spring IoC 容器检索对象,还支持列表的投影、选择以及聚合等。
依赖关系图如下:
2.2.2 数据访问/集成
数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
l JDBC 模块提供了 JDBC 抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
l ORM 模块提供了对流行的对象关系映射 API 的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring的其它功能整合,比如前面提及的事务管理。
l OXM 模块提供了对 OXM 实现的支持,比如 JAXB、Castor、XML Beans、JiBX、XStream 等。
l JMS 模块包含生产(produce)和消费(consume)消息的功能。从 Spring 4.1 开始,集成了 spring-messaging 模块。
l 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写 beginTransaction()、commit()、rollback() 等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细)
2.2.3 Web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
l Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
l Web-MVC 模块为 web 应用提供了模型视图控制(MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
l Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
l Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 spring-webmvc 模块的功能。
2.2.4 其他
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
l AOP 模块提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
l Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
l Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
l Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
l 测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
3.Spring的IOC的使用 IOC、DI
IOC:控制反转。将对象的创建、初始化、销毁等一系列的生命周期过程交给spring管理。
结婚:
方式1: 自己找女生--------------->吃饭、逛街、看电影、送回家等--------->结婚
(同学、同事、公交地铁1个月-3个月) 半年-1年半 1天
方式2: 媒婆(1个月)------------------------>结婚(1天)
吃饭:
方式1: 买菜、买米(30min-1h)---->蒸饭、洗菜、切菜、炒菜(1个小时)—>吃(15-30min)
方式2: 定外卖----------------->吃(15-30min)
面向过程----->面向对象----->面向接口----->面向组件(面向切面)–>面向服务—>面向百度
3.1 基本使用
3.1.1 创建项目并导入spring IoC相关jar包
在pom文件中,引入Spring的IoC核心jar包
<!--依赖-->
<dependencies><!--引入spring的context依赖,可以传递出aop beans core expression--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.4</version></dependency>
</dependencies>
3.1.2 创建User类
package com.bjpowernode.domain;import java.util.Date;/*** Created on 2021/7/16** @author 雷哥*/
public class User {private Integer id;private String name;private Integer age;private String address;private Date birth;public User() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public Date getBirth() {return birth;}public void setBirth(Date birth) {this.birth = birth;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", address='" + address + '\'' +", birth=" + birth +'}';}
}
3.1.3 创建Spring的核心配置文件spring-context.xml/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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- spring 核心配置文件 配置IOC容器中需要创建的bean --><bean id="userId" name="user" class="com.bjpowernode.domain.User" />
</beans>
3.1.4 编写测试程序
public class Test {public static void main(String[] args) {//根据 spring的配置文件 创建 应用容器ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");System.out.println("马上获取bean");//从容器中获取 对象User user = (User) context.getBean("user");System.out.println(user);}
}
3.2 Bean标签属性介绍
3.2.1 id
是 bean的唯一标识 一个bean,其id 值只能有一个 。整个IOC 容器id 值不允许重复,使用名称作为key。
3.2.2 name
一个bean的名称,可以存在多个,多个之间使用逗号分隔。不论bean有没有定义name属性,默认id都会当做name。
3.2.3 class
bean的具体的类型,包名和类名组成。
3.2.4 scope
bean的作用域:如果不写scope,则默认为单例
prototype :非单例,每次获取都会创建一个新的bean对象。
singleton : 单例,多次获取永远同一个bean, 默认值。
request : 一次请求,基于web项目的bean的作用域。
session : 一次会话,基于web项目的bean的作用域。
3.2.5 lazy-init
延迟初始化(懒加载),默认只要加载了配置文件。bean对象就会被初始化,lazy-init则是获取时才会初始化。只针对单例模式有效,非单例每次获取都会创建,没有延迟初始化的意义
3.2.6 depends-on
初始化时依赖的对象,当前对象初始化前需先初始化depends-on指定的对象
3.2.7 init-method
对象初始化后,调用的方法
3.2.8 destroy-method
对象销毁时,调用的方法
3.2.9 autowire
属性自动装配
byName 根据属性名称装配
byType 根据类型装配
3.2.10 autowire-candidate
是否允许作为自动装配的候选项
true 作为自动装配的候选项
false 不作为自动装配的候选项
3.2.11 primary
优先使用该bean,因为Spring需要支持使用类型查找对象,在一个大类型下,可能存在多个小类型。如果根据大类型装配属性时,不知道使用哪个具体的对象,则可以根据primary设置优先级。
3.2.12 代码示例
<?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"><!-- spring 核心配置文件 配置IOC容器中需要创建的bean --><!-- <bean id="userId" name="user" class="com.bjpowernode.domain.User" />--><!--id : bean的唯一标识 整个IOC容器不能重复。name : bean的key,多个name之间使用逗号,class : 具体的bean的全路径scope : bean的作用域singleton 单例 默认prototype 非单例lazy-init="true" 获取时创建对象depends-on="test" 默认自上而下创建 depends-on 会优先创建 depends-on 对应的beaninit-method : 对象创建后调用的方法destroy-method :对象销毁时调用的方法, 容器调用closeautowire : 属性自动装配byName 根据属性名装配byType 根据属性类型装配primary : 当存在多个同样的类型时, primary 为true 则优先使用该bean--><bean id="userId2" name="user1,user2" class="com.bjpowernode.domain.User" scope="singleton" depends-on="test" init-method="init" destroy-method="destory" primary="true" /><!-- test类 --><bean name="test" class="com.bjpowernode.domain.Test" /><bean name="userService1" class="com.bjpowernode.service.impl.UserServiceImpl1" primary="true" /><bean name="userService2" class="com.bjpowernode.service.impl.UserServiceImpl2" />
</beans>package com.bjpowernode.service;public interface IUserService {
}package com.bjpowernode.service.impl;import com.bjpowernode.service.IUserService;/*** @Description: 接口实现类1* @author: Mr.T* @date 2020-09-26 14:33*/
public class UserServiceImpl1 implements IUserService {
}package com.bjpowernode.service.impl;import com.bjpowernode.service.IUserService;
public class UserServiceImpl2 implements IUserService {
}package com.bjpowernode.domain;
public class User {private Integer id;private String name;public User(){System.out.println("构造方法执行 user 对象进行创建");}public void sleep(){System.out.println("早睡早起!!!");}public void init(){System.out.println("对象初始化后调用的方法");}public void destory(){System.out.println("对象销毁时调用");}
}package com.bjpowernode.test;import com.bjpowernode.domain.User;
import com.bjpowernode.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {//根据 spring的配置文件 创建 应用容器ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");System.out.println("马上获取bean");//从容器中获取 对象// 装备对象 : XX XXX1 XX2 xx3User user = (User) context.getBean("user2");//System.out.println("21:"+user);System.out.println("22:"+context.getBean("user2"));//获取接口的类型System.out.println(context.getBean(IUserService.class));context.close();//关闭容器 此时会调用销毁的方法}
}
3.3 Bean对象创建的4种方式
3.3.1 构造方法创建
使用构造方法创建bean对象,是spring默认的创建方式。
<!-- 使用构造方法创建对象 -->
<bean id="user" class="com.bjpowernode.domian.User" />
3.3.2 静态工厂创建
<!-- 使用静态工厂创建对象 -->
<!--id : bean 的唯一标识class : 工厂类factory-method : 工厂方法-->
<bean id="user2" class="com.bjpowernode.factory.UserStaticFactory" factory-method="getObj" />package com.bjpowernode.factory;import com.bjpowernode.domian.User;public class UserStaticFactory {/*** 静态工厂中用于创建对象的方法* @return*/public static User getObj(){System.out.println("静态工厂中 创建对象的方法 执行了");return new User();}
}
3.3.3 非静态工厂创建
<!--非静态工厂创建对象在非静态工厂中,创建对象的方法是非静态方法。非静态方法的执行,首先需要该类对象注意: 使用非静态工厂创建对象,首先需要创建工厂类对象
-->
<!-- 工厂类对象 -->
<bean id="userFactory" class="com.bjpowernode.factory.UserFactory" />
<!-- 使用非静态工厂创建对象 -->
<!--factory-bean : 非静态工厂对象factory-method : 创建对象的非静态方法
-->
<bean id="user3" factory-bean="userFactory" factory-method="getObj" />package com.bjpowernode.factory;import com.bjpowernode.domian.User;public class UserFactory {/*** 工厂中用于创建对象的方法* @return*/public User getObj(){System.out.println("非静态工厂中创建对象的方法 执行了");return new User();}
}
3.3.4 注解创建
Spring为简化对象的创建方式,提供了注解。
3.3.4.1 组件注解:
3.3.4.1.1 @Component(“bs”)
表示该类为一个被Spring管理的组件。但是,由于在开发中为了让代码的可读性更高。
Spring基于分层思想,将需要创建的组件分为以下几类:
3.3.4.1.2 @Controller
@Controller注解,标识该类是controller层的类。并且,注意在使用SpringMVC时,所有的Constroller,必须使用@Controller注解。
3.3.4.1.3 @Service
@Service注解,标识该类是业务层的类。
3.3.4.1.4 @Respository
@Respository注解,标识该类是操作数据层的类。
注意:
以上注解是Spring中定义的创建对象的注解,都可以创建对象,如果该类有明确的作用,有自己所属的层,则建议使用相应的注解,如果实在无法区分该类所属层,可以使用@Component注解。
3.3.4.2 注解使用步骤
3.3.4.2.1 开启组件扫描
在spring的核心配置文件中,开启注解扫描,让Spring将被注解修饰的类,创建对相关。
xml头部
xmlns:context=“http://www.springframework.org/schema/context”
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--开启组件扫描-->
<context:component-scan base-package="com.bjpowernode.*" />
3.3.4.2.2 添加注解
package com.bjpowernode.domian;import org.springframework.stereotype.Component;
@Component
public class Person {public Person(){System.out.println("Person的构造方法.........");}
}
3.3.5 什么时候使用XML配置和注解
能使用注解时,就使用了注解,注解非常方便。但是,在第三方的类中,是无法使用注解的。因为无法在别人提供的源码上加上Spring注解,此时只能使用XML配置的形式,配置第三方类的Bean信息。
3.4 IOC属性注入的3种方式
为对象属性设置值,就是属性注入。
3.4.1 构造方法属性注入:DI(依赖注入,给对象的属性赋值)
<?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 class="com.bjpowernode.domain.User" /><!--有参数的构造方法此时是2个参数的构造方法index : 参数下标 从 0开始value : 属性的值--><bean id="user2" class="com.bjpowernode.domain.User" ><constructor-arg index="0" value="1001" /><constructor-arg index="1" value="韩梅梅" /></bean><!-- 有参数的构造方法使用index下标查找 属性 存在问题 都只有一个参数 则默认使用后面的构造方法可以使用 type 指定参数的类型更推荐 使用name属性 : name表示构造器中参数的名称--><bean id="user3" class="com.bjpowernode.domain.User"><constructor-arg index="0" value="1001" type="java.lang.Integer" /></bean><bean id="user4" class="com.bjpowernode.domain.User"><constructor-arg name="name" value="韩梅梅" /></bean>
</beans>package com.bjpowernode.domain;public class User {/*** id 属性*/private Integer id;/*** name 属性*/private String name;public User(){System.out.println("无参数构造方法");}public User(Integer id) {System.out.println("id参数方法");this.id = id;}public User(String name) {System.out.println("name参数构造方法");this.name = name;}public User(Integer id, String name) {System.out.println("id,name参数构造方法");this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
3.4.2 set方法属性注入
<?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"><!--使用set方法进行属性注入property 表示属性name : 表示属性对应的set方法名,去掉set前缀,首字母小写。并不是真正的属性名--><bean id="dg" class="com.powernode.pojo.Dog"><property name="sonList"><list><value>小黑</value><value>小白</value><value>小花</value></list></property><property name="wifeList"><set><value>小翠儿</value><value>如花</value></set></property><property name="friendMap"><map><entry key="uName1" value="土狗"></entry><entry key="uName2" value="泰迪"></entry><entry key="uName3" value-ref="u1"></entry></map></property><property name="props"><props><prop key="strain">土狗</prop><prop key="age">15</prop><prop key="gender">公</prop></props></property>
</bean></beans>
3.4.3 注解属性注入
在spring中,为了简化属性的注入,Spring提供注解:@Autowired,Spring会自动从IOC容器中,为这个属性查找相应类型的值,进行注入。
-
开启包的注解扫描
<context:component-scan base-package=“com.*” /> -
使用注解
package com.bjpowernode.domain;import org.springframework.beans.factory.annotation.Autowired;import java.util.Date;
public class Student {public Integer id;public String name;@Autowired //使用注解自动注入//User 对象public User user;public void setStudentId(Integer id) {System.out.println("set方法被调用了............");this.id = id;}/* public void setUser(User user) {this.user = user;}*/
}
注意:
在使用自动注入时,可以在bean标签上,配置autowire,但是此时必须有该属性的set方法,@Autowired注解,是不需要set方法的。
如果是在xml中注入对象,值使用ref属性。value属性,只支持boolean,数字,字符串等。
3.5 常见类型的属性注入
在Spring中,提供了丰富的标签,进行各种属性的注入。常见的类型:
数字、字符串、boolean、数组、set、list、map、properties。
package com.bjpowernode.domain;import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;public class Person {private Integer id;private String name;private boolean sex;// true 男 false 女private String[] likes;//爱好private Set<String> girlFriends; //女朋友private List<Dream> dreams;//梦想private Map<String,String> house; //房子private Properties properties; //配置文件属性public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public boolean getSex() {return sex;}public void setSex(boolean sex) {this.sex = sex;}public String[] getLikes() {return likes;}public void setLikes(String[] likes) {this.likes = likes;}public Set<String> getGirlFriends() {return girlFriends;}public void setGirlFriends(Set<String> girlFriends) {this.girlFriends = girlFriends;}public List<Dream> getDreams() {return dreams;}public void setDreams(List<Dream> dreams) {this.dreams = dreams;}public Map<String, String> getHouse() {return house;}public void setHouse(Map<String, String> house) {this.house = house;}public Properties getProperties() {return properties;}public void setProperties(Properties properties) {this.properties = properties;}
}package com.bjpowernode.domain;public class Dream {private String title;public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}
}<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 简单的属性注入 : 基本数据类型 + string --><bean id="person1" class="com.bjpowernode.domain.Person" ><!-- Integer 类型的属性注入 --><property name="id" value="1001" /><!-- String 类型的属性注入 --><property name="name" value="佚名" /><!-- boolean 属性注入 --><property name="sex" value="true" /></bean><!-- 数组类型 --><bean id="person2" class="com.bjpowernode.domain.Person" ><property name="likes" ><array><value>足球</value><value>篮球</value><value>羽毛球</value></array></property></bean><!-- set 类型注入 --><bean id="person3" class="com.bjpowernode.domain.Person" ><property name="girlFriends" ><set><value>韩梅梅</value><value>Lucy</value><value>Lucy</value><value>Rose</value></set></property></bean><bean id="dream1" class="com.bjpowernode.domain.Dream"><property name="title" value="数钱数到手抽筋" /></bean><!-- list 类型 --><bean id="person4" class="com.bjpowernode.domain.Person" ><property name="dreams" ><list><bean class="com.bjpowernode.domain.Dream"><property name="title" value="解放全人类" /></bean><bean class="com.bjpowernode.domain.Dream"><property name="title" value="世界和平" /></bean><ref bean="dream1"/></list></property></bean><!-- map结构 --><bean id="person5" class="com.bjpowernode.domain.Person" ><property name="house" ><map><entry key="wh" value="江滩" /><entry key="bj" value="后海" /><entry key="hz" value="西湖" /></map></property></bean><!-- properties --><bean id="person6" class="com.bjpowernode.domain.Person" ><property name="properties" ><props><prop key="driver">驱动</prop><prop key="url">url</prop></props></property></bean>
</beans>package com.bjpowernode.test;import com.bjpowernode.domain.Dream;
import com.bjpowernode.domain.Person;
import com.bjpowernode.domain.Student;
import com.bjpowernode.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.Arrays;
import java.util.List;
import java.util.Properties;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");Person person1 = (Person) context.getBean("person1");System.out.println(person1.getId() +" "+ person1.getName() + " "+ person1.getSex() );Person person2 = (Person) context.getBean("person2");String[] likes = person2.getLikes();System.out.println(Arrays.asList(likes));Person person3= (Person) context.getBean("person3");System.out.println(person3.getGirlFriends());Person person4 = (Person) context.getBean("person4");List<Dream> dreams = person4.getDreams();System.out.println(dreams.get(0).getTitle());System.out.println(dreams.get(1).getTitle());System.out.println(dreams.get(2).getTitle());Person person5 = (Person) context.getBean("person5");System.out.println(person5.getHouse());Person person6 = (Person) context.getBean("person6");Properties properties = person6.getProperties();System.out.println(properties.getProperty("driver"));System.out.println(properties.getProperty("url"));}
}
3.6 使用IOC容器,改造传统项目
3.6.1 xml配置版
3.6.1.1 创建项目并加入依赖
<!--依赖-->
<dependencies><!--引入spring的context依赖,可以传递出aop beans core expression--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.4</version></dependency>
</dependencies>
3.6.1.2 项目结构
3.6.1.3 配置文件
<?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"><!-- 声明 com.bjpowernode.dao 对象 --><bean id="userDao" class="com.bjpowernode.dao.UserDao" /><!-- 声明service 对象 --><bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType"><!-- 使用自动装配 则不需要 声明属性注入 但是要有set方法 --><!--<property name="userDao" ref="userDao" />--></bean><!-- 声明controller --><bean id="userController" class="com.bjpowernode.controller.UserController" autowire="byType"><!-- 使用自动装配 则不需要 声明属性注入 但是要有set方法--><!--<property name="userService" ref="userService"/>--></bean>
</beans>
3.6.1.4 controller
package com.bjpowernode.controller;import com.bjpowernode.service.IUserService;public class UserController {private IUserService userService;public void register(){System.out.println("控制层的 register 方法");userService.register();}public void setUserService(IUserService userService) {this.userService = userService;}
}
3.6.1.5 dao
package com.bjpowernode.dao;
public class UserDao {public void add(){System.out.println("进行数据库数据新增操作");}
}
3.6.1.6 service
package com.bjpowernode.service;public interface IUserService {/*** 注册接口*/public void register();
}package com.bjpowernode.service.impl;import com.bjpowernode.dao.UserDao;
import com.bjpowernode.service.IUserService;public class UserServiceImpl implements IUserService {/*** com.bjpowernode.dao 层 属性*/private UserDao userDao;@Overridepublic void register() {System.out.println("com.bjpowernode.service 的 register ");userDao.add();}/*** 使用set方法 是为了自动装配 或者进行set 属性设置* @param userDao*/public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
3.6.1.7 test
package com.bjpowernode;
import com.bjpowernode.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");UserController bean = context.getBean(UserController.class);bean.register();}
}
3.6.2 注解版
3.6.2.1 配置文件
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 开启组件扫描 --><context:component-scan base-package="com.*" />
</beans>
3.6.2.2 controller
package com.bjpowernode.controller;import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {@Autowiredprivate IUserService userService;public void register(){System.out.println("控制层的 register 方法");userService.register();}
}
3.6.2.3 dao
package com.bjpowernode.dao;import org.springframework.stereotype.Repository;@Repository
public class UserDao {public void add(){System.out.println("进行数据库数据新增操作");}
}
3.6.2.4 service
package com.bjpowernode.service;public interface IUserService {/*** 注册接口*/public void register();
}package com.bjpowernode.service.impl;import com.bjpowernode.dao.UserDao;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements IUserService {/*** com.bjpowernode.dao 层 属性*/@Autowiredprivate UserDao userDao;@Overridepublic void register() {System.out.println("com.bjpowernode.service 的 register ");userDao.add();}
}
3.6.2.5 test
package com.bjpowernode.test;import com.bjpowernode.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");//UserController bean = context.getBean(UserController.class);//bean.register();//程序员自己创建的对象 不在IOC 自己容器中//new UserController().register();}
}
4.AOP
com.powernode.aadenglu
Login: login():登录方法
com.powernode.anquan
Security: isSecurity():检测操作环境是否安全
com.powernode.bmapper
CBCBankMapper(核心类): selectMoney() updateMoney() updateInvest() update2Tel()
com.powernode.crizhi
Logger: log():记录用户操作细节
com.powernode.dqinli
Clean :cleanResource():清理缓存
Expt: handExpt()
4.1 AOP简介:面向切面(面向组件)
DefaultAopProxyFactory
代理
静态代理
静态代理,每个被代理类都需要创建对应的代理类。随着程序的扩展,代理类也会增多,臃肿,维护量变多,为了解决这个问题,Java中,提供了动态代理技术,开发者不需要自己定义代理类,代理类由JDK动态的创建,开发只需要指定被代理的类即可。
切面(aspect):除了核心类以外的其他类称之为切面
通知(advisor):切面中的方法称之为通知
核心类:一个项目中不能省略的模块类
核心方法:核心类中的方法称之为核心方法
连接点:核心类中的某一个方法
切入点(pointcut):某个包下的某个类下的某一批方法
代理(proxy):将多个切面与核心类组合在一起,形成一个新的类,这个类称之为代理类
织入(weaver):书写代理类的过程称之为织入
4.2 动态代理
4.2.1 JDK动态代理
4.2.1.1 Proxy
该类提供了方法创建代理类和代理类的对象的方法
创建一个代理类并返回代理类对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader : 类加载器,指定类加载器,是为了精确的定位类
interfaces : 接口Class类,使用JDK的反射,必须要有接口
h :InvocationHandler ,代理的处理器,每个代理类都有一个关联的处理器
4.2.1.2 InvocationHandler
是每个代理类对应的处理器
Object 方法调用的返回值,可以作为被代理的方法调用的返回值
proxy : 代理类对象
method : 目标类中被代理的方法
args : 目标类中被代理的方法的运行参数
Object invoke(Object proxy,Method method,Object[] args)
4.2.1.3 代码示例
4.2.1.3.1 目标类接口
package com.bjpowernode.proxy.demo02;/*** @Description: 目标类接口* @author: Mr.T* @date 2020-09-27 10:38*/
public interface ITargetClass {/*** 房子出租* @param m*/void rent(int m);
}
4.2.1.3.2 目标类
package com.bjpowernode.proxy.demo02;/*** @Description: 目标类* @author: Mr.T* @date 2020-09-27 10:39*/
public class TargetClass implements ITargetClass {@Overridepublic void rent(int m) {System.out.println("出租的金额为:" + m);}
}
4.2.1.3.3 代理类处理器
package com.bjpowernode.proxy.demo02;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @Description: 代理的处理器* 处理器会被绑定一个代理* 帮助代理调用目标方法* @author: Mr.T* @date 2020-09-27 10:40*/
public class ProxyHanlder implements InvocationHandler {/*** 目标方法类的对象*/private Object targetObj;public ProxyHanlder(Object targetObj){this.targetObj = targetObj;}/**** @param proxy 生成的代理类的对象* @param method 目标类中被代理的方法* @param args 目标类中被代理的方法实际参数* @return 可以当做目标方法的返回值* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//使用反射调用 目标类中的方法Object obj = method.invoke(targetObj, args);return obj;}
}
4.2.1.3.4 测试类
package com.bjpowernode.proxy.demo02;import java.lang.reflect.Proxy;/*** @Description: TODO* @author: Mr.T* @date 2020-09-27 10:45*/
public class Test {public static void main(String[] args) {System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");//创建了目标类对象ITargetClass targetClass = new TargetClass();//创建处理器ProxyHanlder proxyHanlder = new ProxyHanlder(targetClass);//创建具体的代理类和对象 具体产生的代理类 会实现 接口 所以能够转化为 接口类型ITargetClass proxy = (ITargetClass) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{ITargetClass.class}, proxyHanlder);// 调用代理类中 rent方法 $proxy0proxy.rent(100);}
}
4.2.1.3.5 生成代理类源码
package com.bjpowernode.proxy.demo02;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.UndeclaredThrowableException;/*** @Description: 动态代理生成的代理类* Proxy : JDK中所有生成的代理的父类* ITargetClass : 指定被代理类的接口** @author: Mr.T* @date 2020-09-27 10:59*/
public final class $Proxy0 extends Proxy implements ITargetClass
{private static Method m0; //hashCode 方法private static Method m1; // equalsprivate static Method m2; //toStringprivate static Method m3; // rent 方法 被代理的方法static {try {$Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);$Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));$Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);$Proxy0.m3 = Class.forName("com.bjpowernode.proxy.demo02.ITargetClass").getMethod("rent", Integer.TYPE);}catch (NoSuchMethodException ex) {throw new NoSuchMethodError(ex.getMessage());}catch (ClassNotFoundException ex2) {throw new NoClassDefFoundError(ex2.getMessage());}}/*** invocationHandler 就是定义的 invocationHandler* @param invocationHandler*/public $Proxy0(final InvocationHandler invocationHandler) {super(invocationHandler); //Proxy(InvocationHandler invocationHandler) --> 为父类中 InvocationHandler 赋值//当前类中 InvocationHandler 对象 且有值/*Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}* */}public final boolean equals(final Object o) {try {return (boolean)super.h.invoke(this, $Proxy0.m1, new Object[] { o });}catch (Error | RuntimeException error) {throw;}catch (Throwable t) {throw new UndeclaredThrowableException(t);}}public final String toString() {try {return (String)super.h.invoke(this, $Proxy0.m2, null);}catch (Error | RuntimeException error) {throw;}catch (Throwable t) {throw new UndeclaredThrowableException(t);}}public final void rent(final int n) {try {//super.h this.h ---> 自定义的 ProxyHanlder//调用自定义的ProxyHanlder 中的invoke方法 : $Proxy0 对象//$Proxy0.m3 rent 方法//new Object[] { n } 传入的参数super.h.invoke(this, $Proxy0.m3, new Object[] { n });}catch (Error | RuntimeException error) {throw;}catch (Throwable t) {throw new UndeclaredThrowableException(t);}}public final int hashCode() {try {return (int)super.h.invoke(this, $Proxy0.m0, null);}catch (Error | RuntimeException error) {throw;}catch (Throwable t) {throw new UndeclaredThrowableException(t);}}
}
4.2.1.3.6 JDK动态代理的不足
在JDK中使用动态代理,必须有类的接口。因为生成的代理需要实现这个接口,这样我们生成的代理类对象,才能转化为代理目标的接口对象,然后根据接口中的方法,调用处理器中invoke方法。
4.2.2 Cglib动态代理
为了弥补JDK动态代理的不足,第三方组织封装一套工具包,cglib的工具包,这套包不基于接口,基于父子继承,通过重写的形式扩展方法,但是这个子类工具自动生成的。
早期,Cglib动态代理,性能相于JDK的动态代理高一些。JDK进行一些列优化,目前Spring默认使用的动态代理JDK,也支持Cglib。
4.2.2.1 Cglib动态代理的使用
4.2.2.1.1 MethodInterceptor
cglib中,提供的对方法执行拦截的接口。其中intercept是对具体方法进行拦截处理的方法。
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy)
Object : 方法执行返回的结果
obj :增强类的对象
method :目标方法
proxy :用于回调的方法的对象
4.2.2.1.2 代码示例
4.2.2.1.2.1 导入jar包
<!-- 引入cglib 的jar 包-->
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
4.2.2.1.2.2 创建被代理目标类
package com.bjpowernode.proxy.demo03;/*** @Description: 被代理的目标类* @author: Mr.T* @date 2020-09-27 14:13*/
public class TargetClass {public void rent(){System.out.println("目标类中的出租方法");}
}
4.2.2.1.2.3 方法拦截器
package com.bjpowernode.proxy.demo03;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** @Description: 方法执行的拦截器* @author: Mr.T* @date 2020-09-27 14:20*/
public class MyMethodInteceptor implements MethodInterceptor {/*** 进行具体的拦截的方法* @param obj 被代理类的对象* @param method 被代理的目标方法* @param args 实际运行的参数* @param proxy 代理类对象* @return* @throws Throwable*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("前置增强");//代理 调用父类中的方法Object o = proxy.invokeSuper(obj, args);System.out.println("intercept 执行了");//执行了System.out.println("后置增强");return o ;}
}
4.2.2.1.2.4 测试类
package com.bjpowernode.proxy.demo03;import net.sf.cglib.proxy.Enhancer;/*** @Description: TODO* @author: Mr.T* @date 2020-09-27 14:21*/
public class Test {public static void main(String[] args) {//增强类工具 可以创建代理类对象Enhancer enhancer = new Enhancer();enhancer.setSuperclass(TargetClass.class);//设置调用时 回调enhancer.setCallback(new MyMethodInteceptor());//创建代理类对象TargetClass proxy = (TargetClass) enhancer.create();proxy.rent();}
}
4.2.2.2 动态代理的不足
不论是JDK的动态代理,还是第三方cglib动态代理,都需要开发者编写代码处理程序。程序结构基本上大同小异,重复造轮子。基于这样的情况,在Spring中,提供了2种方式:xml配置形式和注解形式,使用动态代理。这种模式就是Spring Aop技术。其底层依然是动态代理。
4.3 Spring的AOP配置
在Spring中,AOP的配置主要分为2类:xml配置和注解配置
XML配置也分为两种,一种Spring的原生支持,一种是Spring的aspects这个相关的框架。
4.3.1 AOP的相关概念
连接点(JoinPoint):所谓连接点是指那些被拦截的点,而spring中这些点就是指方法,因为spring只支持方法类型的连接点。
切入点(PointCut):所谓切入点就是指我们要对那些JoinPoint进行拦截的定义,指的是具体的拦截的位置
增强/通知(Advice) : 增强就是对具体的连接点进行扩展的功能。由于一般对方法进行增强,分为在方法前执行或者方法后,或者发生异常执行等等,所以增强被分为:前置增强(前置通知)、后置增强(后置通知)、环绕通知(环绕增强)、异常增强(异常通知)
引介(Introduction):引介是一种特殊的Advice,在不修改代码的前提下,引介可以在运行期为类动态的添加一些方法或Field.
目标(Target) :被代理的类(需要增强类)
织入(Weaving) :把Advice应用到Target的过程
代理(Proxy):使用AOP配置后产生的代理类
切面(Aspect):切点和增强整合形成了切面
4.3.2 Spring自身AOP具体配置
4.3.2.1 引入aop相关jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>02-spring-aop01</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><spring.version>5.2.0.RELEASE</spring.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>${spring.version}</version></dependency></dependencies></project>
4.3.2.2 定义增强类
4.3.2.2.1 前置增强-MethodBeforeAdvice
package com.bjpowernode.advice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;/*** @Description: spring 规定的增强方法接口* @author: Mr.T* @date 2020-09-27 15:42*/
public class MyBeforeAdvice implements MethodBeforeAdvice {/**** @param method 目标方法* @param args 实际运行的参数* @param target 目标类对象* @throws Throwable*/@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("前置增强的方法");}
}
4.3.2.2.2 后置增强-AfterReturningAdvice
package com.bjpowernode.advice;import org.springframework.aop.AfterReturningAdvice;import java.lang.reflect.Method;/*** @Description: 后置增强接口* @author: Mr.T* @date 2020-09-27 15:55*/
public class MyAfterAdvice implements AfterReturningAdvice {/**** @param returnValue 被增强的方法运行后返回的数据* @param method 被增强的方法* @param args 方法运行的参数* @param target 目标类对象* @throws Throwable*/@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("后置增强方法");}
}
4.3.2.2.3 环绕增强-MethodInterceptor
package com.bjpowernode.advice;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;/*** @Description: 环绕增强* @author: Mr.T* @date 2020-09-27 16:04*/
public class MyAroundAdvice implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("前置增强");Object rs = invocation.proceed();//调用 目标方法System.out.println("后置增强");return rs;}
}
4.3.2.2.4 异常增强-ThrowsAdvice
异常增强中的增强方法必须叫:afterThrowing,并且必须定义参数接收发生的异常信息。
package com.bjpowernode.advice;import org.springframework.aop.ThrowsAdvice;/*** @Description: 异常增强类* @author: Mr.T* @date 2020-09-27 16:12*/
public class MyExceptionAdvice implements ThrowsAdvice {public void afterThrowing(Exception ex){System.out.println("异常增强的方法!!!!!!");}
}
4.3.2.3 目标类
package com.bjpowernode;public interface ITargetClass {/*** 待增强的目标方法*/void targetMethod();/*** 待增强的方法* @param msg* @return*/String afterTargetMethod(String msg);/*** 环绕增强的方法*/void aroundTargetMethod();/*** 执行会发生异常*/void runException();
}package com.bjpowernode.impl;import com.bjpowernode.ITargetClass;/*** @Description: 目标类* @author: Mr.T* @date 2020-09-27 15:38*/
public class TargetClassImpl implements ITargetClass {@Overridepublic void targetMethod() {System.out.println("待增强的目标方法");}@Overridepublic String afterTargetMethod(String msg) {System.out.println("待增强的目标方法 --- afterTargetMethod");return "被增强的方法的返回值";}public void aroundTargetMethod() {System.out.println("待增强的目标方法 --- aroundTargetMethod");}/*** 发生异常*/public void runException() {System.out.println("待增强的目标方法 --- runException");int m = 0;int n = 100/m;}}
4.3.2.4 aop配置
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定义目标类对象 --><bean id="targetClass" class="com.bjpowernode.impl.TargetClassImpl" /><!-- 定义增强类对象 前置增强类 --><bean id="myBeforeAdvice" class="com.bjpowernode.advice.MyBeforeAdvice" /><!-- 定义增强类对象 后置增强 --><bean id="myAfterAdvice" class="com.bjpowernode.advice.MyAfterAdvice" /><!-- 定义增强类 环绕增强 --><bean id="myAroundAdvice" class="com.bjpowernode.advice.MyAroundAdvice" /><!-- 定义增强类 异常增强类 --><bean id="myExceptionAdvice" class="com.bjpowernode.advice.MyExceptionAdvice" /><!-- 进行织入 --><aop:config><!--id : 连接点的唯一标识expression : 连接点的表达式execution(* 包名.类名.方法名(..))* 指任意字符.. 表示参数可以是任意个--><aop:pointcut id="beforePoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.targetMethod(..))"/><!-- 后置增强的切点 --><aop:pointcut id="afterPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.afterTargetMethod(..))"/><!-- 环绕增强的切点 --><aop:pointcut id="aroundPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.aroundTargetMethod(..))"/><!-- 异常增强的切点 --><aop:pointcut id="exceptionPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.runException(..))"/><!--织入将增强和连接点 结合--><aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="beforePoint" /><!-- 织入后置增强的织入--><aop:advisor advice-ref="myAfterAdvice" pointcut-ref="afterPoint" /><!--织入环绕增强的织入--><aop:advisor advice-ref="myAroundAdvice" pointcut-ref="aroundPoint" /><!--织入异常增强的织入--><aop:advisor advice-ref="myExceptionAdvice" pointcut-ref="exceptionPoint" /></aop:config></beans>
4.3.2.5 测试类
package com.bjpowernode;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");ITargetClass targetClass = context.getBean(ITargetClass.class);targetClass.runException();}
}
4.3.3 AspectJ框架AOP配置
在原生的spring中,每种增强都需要单独定义一个类实现相应的接口。增强类本身就更庞大,而且方法的名称是固定的。基于这种情况,AspectJ提供了相对更加灵活的方式。
在AspectJ中,只需要定义一个增强类即可,并且方法的名称可以任意定义。
4.3.3.1 引入相关jar
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>02-spring-aop02</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><spring.version>5.2.0.RELEASE</spring.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>${spring.version}</version></dependency></dependencies>
</project>
4.3.3.2 编写增强类
package com.bjpowernode.advice;import org.aopalliance.intercept.Invocation;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;public class MyAdvice {public void beforAdvice(){System.out.println("前置增强的方法");}public void afterAdvice(String name,String rs){System.out.println("后置增强的方法");}public void aroundAdvice(ProceedingJoinPoint joinPoint){System.out.println("前置增强");try {joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("后置增强");}/*** 异常增强* @param exception*/public void exceptionAdvice(Exception exception){System.out.println("异常增强!");}
}
4.3.3.3 编写目标类
package com.bjpowernode;public interface ITargetClass {/*** 前置增强的方法*/void beforeMethod();/*** 后置增强的方法*/String afterMethod(String name1);/*** 环绕增强的方法*/void aroundMethod();/*** 异常增的方法*/void runExceptionMethod();
}package com.bjpowernode.impl;import com.bjpowernode.ITargetClass;public class TargetClassImpl implements ITargetClass {@Overridepublic void beforeMethod() {System.out.println("待前置增强--------beforeMethod");}@Overridepublic String afterMethod(String name) {System.out.println("待后置增强--------afterMethod");return "韩梅梅";}@Overridepublic void aroundMethod() {System.out.println("待环绕增强--------aroundMethod");}@Overridepublic void runExceptionMethod() {System.out.println("待异常增强--------runExceptionMethod");int m = 0;int n = 100/m;}
}
4.3.3.4 配置AspectJ的增强配置
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定义增强的目标类对象 --><bean id="targetClass" class="com.bjpowernode.impl.TargetClassImpl" /><!-- 定义增强类的对象 --><bean id="myAdvice" class="com.bjpowernode.advice.MyAdvice" /><!-- 进行AOP配置 --><aop:config><!-- 前置切点 --><!--aop:before : aspectJ中 前置增强的配置method : 当前增强类中前置增强的方法 方法名pointcut-ref : 增强连接点--><aop:pointcut id="beforPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.beforeMethod(..)) " /><!-- 后置切点 --><!-- args 配置被增强的方法的参数名称 --><aop:pointcut id="afterPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.afterMethod(..)) and args(name)" /><!-- 环绕切点 --><aop:pointcut id="aroundPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.aroundMethod(..))" /><!-- 异常切点 --><aop:pointcut id="exceptionPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.runExceptionMethod(..))" /><aop:aspect ref="myAdvice"><!-- <aop:before method="beforAdvice" pointcut-ref="beforPoint" />--><!--arg-names :后置增强中增强的方法的参数名称注意: name 也是被增强的方法的参数名称 参数名称要一致returning :返回结果的参数名称--><aop:after-returning method="afterAdvice" pointcut-ref="afterPoint" arg-names="name,rs" returning="rs" /><!--<aop:around method="aroundAdvice" pointcut-ref="aroundPoint" />--><!--throwing : 接收异常参数的名称--><aop:after-throwing method="exceptionAdvice" pointcut-ref="exceptionPoint" throwing="exception" /></aop:aspect></aop:config></beans>
4.3.3.5 进行测试
package com.bjpowernode;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");ITargetClass bean = context.getBean(ITargetClass.class);bean.runExceptionMethod();}
}
4.3.4 AspectJ的AOP注解方式
4.3.4.1 引入相关jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>02-spring-aop3</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><spring.version>5.2.0.RELEASE</spring.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>${spring.version}</version></dependency></dependencies>
</project>
4.3.4.2 定义增强类
package com.bjpowernode.advice;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MyAdvice {@Before(value = "execution(* com.bjpowernode.impl.TargetClassImpl.beforeMethod(..))")public void beforAdvice(){System.out.println("前置增强的方法");}/**args 被增强的方法的参数名称* argNames 增强方法的参数名称* 参数名称必须一致* returning 参数名称* */@AfterReturning(value="execution(* com.bjpowernode.impl.TargetClassImpl.afterMethod(..)) && args(name)",argNames = "name,rs" ,returning = "rs" )public void afterAdvice(String name,String rs){System.out.println("后置增强的方法");}@Around(value="execution(* com.bjpowernode.impl.TargetClassImpl.aroundMethod(..))")public void aroundAdvice(ProceedingJoinPoint joinPoint){System.out.println("前置增强");try {joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("后置增强");}/*** 异常增强* @param exception*/@AfterThrowing(value="execution(* com.bjpowernode.impl.TargetClassImpl.runExceptionMethod(..))",throwing = "exception" )public void exceptionAdvice(Exception exception){System.out.println("异常增强!");}
}
4.3.4.3 目标类
package com.bjpowernode;public interface ITargetClass {/*** 前置增强的方法*/void beforeMethod();/*** 后置增强的方法*/String afterMethod(String name);/*** 环绕增强的方法*/void aroundMethod();/*** 异常增的方法*/void runExceptionMethod();
}package com.bjpowernode.impl;import com.bjpowernode.ITargetClass;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Component
public class TargetClassImpl implements ITargetClass {@Overridepublic void beforeMethod() {System.out.println("待前置增强--------beforeMethod");}@Overridepublic String afterMethod(String name) {System.out.println("待后置增强--------afterMethod");return "韩梅梅";}@Overridepublic void aroundMethod() {System.out.println("待环绕增强--------aroundMethod");}@Overridepublic void runExceptionMethod() {System.out.println("待异常增强--------runExceptionMethod");int m = 0;int n = 100/m;}
}
4.3.4.4 开启相关注解
<?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:context="http://www.springframework.org/schema/context"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/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"><!-- 开启组件扫描 --><context:component-scan base-package="com.*" /><!-- 开启aspectj 的相关注解 --><aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
4.3.4.5 测试类
package com.bjpowernode;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");ITargetClass bean = context.getBean(ITargetClass.class);bean.runExceptionMethod();}
}
5.Spring整合Mybatis
Spring整合Mybatis就是将Mybatis交给Spring进行管理,将Mybatis的SqlSession对象,放入IOC容器中,并且可以利用自动装配功能,为每个数据库操作层,注入SqlSession。并且Spring内置可以有代理的,可以根据SqlSession对象及数据操作接口,创建Mapper接口的代理对象,此时Mapper的代理在IOC容器中,那么可以将Mapper接口的对象注入到Service中。
5.1 多XML版
使用配置文件版本,Mybatis配置文件和Spring配置文件是单独的。
5.1.1 引入相关jar包
5.1.1.1 spring相关jar包
<!-- spring相关jar包 开始 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.0.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>5.2.0.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.0.RELEASE</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.0.RELEASE</version>
</dependency>
<!-- spring相关jar包 结束 -->
5.1.1.2 mybatis相关jar包
<!-- mybatis核心包 开始-->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version>
</dependency>
<!-- mybatis核心包 结束-->
5.1.1.3 数据库相关jar包
<!-- mysql jdbc 数据库包 开始 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!-- mysql jdbc 数据库包 结束 -->
5.1.1.4 日志相关jar包
<!-- 日志相关jar包 开始 -->
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version>
</dependency>
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.13.3</version>
</dependency>
<!-- 日志相关jar包 结束 -->
5.1.1.5 spring和mybatis整合包
<!-- spring和 mybatis 整合包 开始 -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.4</version>
</dependency>
<!-- spring和 mybatis 整合包 结束-->
5.1.1.6 mybatis分页插件包
<!-- mybatis分页插件包 开始-->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.2.0</version>
</dependency>
<!-- mybatis分页插件包 结束-->
5.1.2 相关类
5.1.2.1 domain
package com.bjpowernode.domain;public class User {private Integer id;private String username;private String password;private String realname;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getRealname() {return realname;}public void setRealname(String realname) {this.realname = realname;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", realname='" + realname + '\'' +'}';}
}
5.1.2.2 mapper
package com.bjpowernode.mapper;import com.bjpowernode.domain.User;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserMapper {/*** 根据ID 查询用户* @param id* @return*/User selectById(@Param("id") Integer id);/*** 查询所有* @return*/List<User> selectAll();
}
5.1.2.3 service
package com.bjpowernode.service;import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;public interface IUserService {User queryUser(Integer id);PageInfo<User> queryPage(Integer page,Integer limit);
}package com.bjpowernode.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;public class UserServiceImpl implements IUserService {private UserMapper userMapper;@Overridepublic User queryUser(Integer id) {return userMapper.selectById(id);}@Overridepublic PageInfo<User> queryPage(Integer page,Integer limit) {Page<User> users = PageHelper.startPage(page, limit);userMapper.selectAll();return users.toPageInfo();}/*** 使用xml形式注入 mapper* @param userMapper*/public void setUserMapper(UserMapper userMapper) {this.userMapper = userMapper;}
}
5.1.2.4 测试类
package com.bjpowernode;import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.List;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");IUserService bean = context.getBean(IUserService.class);PageInfo<User> pageInfo = bean.queryPage(2, 1);List<User> list = pageInfo.getList();for (User user : list) {System.out.println(user);}}
}
5.1.3 相关配置文件
5.1.3.1 jdbc配置文件
#数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=UTF8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
5.1.3.2 日志配置文件
# 全局日志配置
log4j.rootLogger=DEBUG, stdout
# MyBatis 日志配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
5.1.3.3 mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 引入数据库配置文件 --><!--<properties resource="jdbc.properties" />--><!-- 全局设置 --><settings><setting name="logImpl" value="LOG4J"/></settings><!-- 配置类别名 --><typeAliases><package name="com.bjpowernode.domain"/></typeAliases><!-- 配置分页插件 --><plugins><plugin interceptor="com.github.pagehelper.PageInterceptor" /></plugins><!-- 数据源环境 -->
<!-- <environments default="dev"><environment id="dev"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments>--><!-- 映射文件 --><mappers><mapper resource="mapper/UserMapper.xml" /></mappers>
</configuration>
5.1.3.4 mybatis映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjpowernode.mapper.UserMapper"><select id="selectById" resultType="com.bjpowernode.domain.User">select * from user where id = #{id}</select><select id="selectAll" resultType="com.bjpowernode.domain.User">select * from user</select>
</mapper>
5.1.3.5 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: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/context https://www.springframework.org/schema/context/spring-context.xsd"><!--终极目标 为 每个service 注入一个Mapper 对象1. Mapper 对象2. SqlSession 获取3. SqlSession 要根据 SqlSessionFactory 获取4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器6. 配置数据源--><!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性--><context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" /><!--2. 配置数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!--3. 配置SqlSessionFactoryBean 对象--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- mybatis 核心配置文件 --><property name="configLocation" value="mybatis.xml" /><property name="dataSource" ref="dataSource" /></bean><!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 --><property name="basePackage" value="com.bjpowernode.mapper" /><!--注入SqlSessionFactoryBean 用于创建SqlSession --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" /></bean><!-- 配置Service 层对象 --><bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" /></beans>
5.2 Spring配置文件版
使用Spring的配置文件,取代mybatis的核心配置文件。
导入的jar包和相关类完全一致,jdbc配置文件和日志配置文件也相同。只是将mybatis的核心配置文件中的配置,移入到spring的核心配置文件中。
5.2.1 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: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/context https://www.springframework.org/schema/context/spring-context.xsd"><!--终极目标 为 每个service 注入一个Mapper 对象1. Mapper 对象2. SqlSession 获取3. SqlSession 要根据 SqlSessionFactory 获取4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器6. 配置数据源--><!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性--><context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" /><!--2. 配置数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!--3. 配置SqlSessionFactoryBean 对象--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 配置数据源 --><property name="dataSource" ref="dataSource" /><!-- 配置包下类别名 多个包之间使用 逗号分隔 --><property name="typeAliasesPackage" value="com.bjpowernode.domain" /><!-- 配置插件 --><property name="plugins"><array><bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" /></array></property><!-- 配置映射文件 --><property name="mapperLocations" value="mapper/**/*Mapper.xml"></property></bean><!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 --><property name="basePackage" value="com.bjpowernode.mapper" /><!--注入SqlSessionFactoryBean 用于创建SqlSession --><!--SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean区分数据源--><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" /></bean><!-- 配置Service 层对象 --><bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" /></beans>
5.2.2 配置日志
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd"><!--终极目标 为 每个service 注入一个Mapper 对象1. Mapper 对象2. SqlSession 获取3. SqlSession 要根据 SqlSessionFactory 获取4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器6. 配置数据源--><!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性--><context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" /><!--2. 配置数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置 Configuration--><bean id="configuration" class="org.apache.ibatis.session.Configuration"><!-- 指定日志工具 --><property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl" /><!-- 配置缓存 --><property name="cacheEnabled" value="true" /></bean><!--3. 配置SqlSessionFactoryBean 对象--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 配置数据源 --><property name="dataSource" ref="dataSource" /><!-- 配置包下类别名 多个包之间使用 逗号分隔 --><property name="typeAliasesPackage" value="com.bjpowernode.domain" /><!-- 配置插件 --><property name="plugins"><array><bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" /></array></property><!-- 注入configuration --><property name="configuration" ref="configuration" /><!-- 配置映射文件 --><property name="mapperLocations" value="mapper/**/*Mapper.xml"></property></bean><!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 --><property name="basePackage" value="com.bjpowernode.mapper" /><!--注入SqlSessionFactoryBean 用于创建SqlSession --><!--SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean区分数据源--><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" /></bean><!-- 配置Service 层对象 --><bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" /></beans>
6.声明式事务
在spring中,spring可以管理数据源,管理连接,spring也可以管理事务,并且spring单独对事务分了一个模块进行管理。并且,Spring简化了事务开发,只需要通过配置的方式,Spring即可对事务进行统一完善的管理,Spring的事务管理基于Spring的AOP技术。
Spring的声明式事务,有两种方式:xml配置、注解
6.1 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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--终极目标 为 每个service 注入一个Mapper 对象1. Mapper 对象2. SqlSession 获取3. SqlSession 要根据 SqlSessionFactory 获取4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器6. 配置数据源--><!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性--><context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" /><!--2. 配置数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置 Configuration--><bean id="configuration" class="org.apache.ibatis.session.Configuration"><!-- 指定日志工具 --><property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl" /><!-- 配置缓存 --><property name="cacheEnabled" value="true" /></bean><!--3. 配置SqlSessionFactoryBean 对象--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 配置数据源 --><property name="dataSource" ref="dataSource" /><!-- 配置包下类别名 多个包之间使用 逗号分隔 --><property name="typeAliasesPackage" value="com.bjpowernode.domain" /><!-- 配置插件 --><property name="plugins"><array><bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" /></array></property><!-- 注入configuration --><property name="configuration" ref="configuration" /><!-- 配置映射文件 --><property name="mapperLocations" value="mapper/**/*Mapper.xml"></property></bean><!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 --><property name="basePackage" value="com.bjpowernode.mapper" /><!--注入SqlSessionFactoryBean 用于创建SqlSession --><!--SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean区分数据源--><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" /></bean><!-- 配置Service 层对象 --><bean id="userService" class="com.bjpowernode.service.impl.UserServiceImpl" autowire="byType" /><!-- 事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 配置数据源 指定要管理那个数据源的事务 --><property name="dataSource" ref="dataSource" /></bean><!-- 配置事务增强 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><!-- 配置增强规则 --><tx:attributes><!--name : 进行数据库操作方法的名称 add* 表示 add开头的方法 * 指代任意字符propagation : 事务传播性 面试重点read-only : 只读事务 默认 falserollback-for : 指定回滚的异常 默认是 RunTimeException 下的异常会自动回滚no-rollback-for : 不回滚的异常timeout : 事务的超时时间isolation : 事务隔离级别 面试重点| 1. 读未提交| 2. 读已提交| 3. 可重复读| 4. 串行化--><tx:method name="add*" read-only=”false” rollback-for="Exception"/><tx:method name="insert*" rollback-for="Exception" /><tx:method name="update*" rollback-for="Exception" /><tx:method name="delete*" rollback-for="Exception" /><tx:method name="*" read-only="true" /></tx:attributes></tx:advice><!-- 配置切面 --><aop:config><!-- 注意一般切点在 service --><aop:pointcut id="mypoint" expression="execution(* com.bjpowernode.service.impl.*.*(..))"/><!-- 织入增强 --><aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint" /></aop:config>
</beans>
6.2 注解版声明式事务
在Spring中,为简化事务开发,提供了注解:@Transactional,该注解可以指定在类上,在类上,该类中的所有方法都会使用事务,该注解也可以指定方法上,该方法会使用事务。使用非常简单,只需:
-
配置事务管理器
-
开启注解事务
-
在需要使用事务的地方使用@Transactional
6.2.1 配置类
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!--终极目标 为 每个service 注入一个Mapper 对象1. Mapper 对象2. SqlSession 获取3. SqlSession 要根据 SqlSessionFactory 获取4. SqlSessionFactory 要 根据 SqlSessionFactoryBuilder获取5. SqlSessionFactoryBuilder 可以直接创建 此时需要使用IOC容器6. 配置数据源--><!--1. 引入jdbc的配置文件 使用配置文件 不是使用系统属性--><context:property-placeholder location="jdbc.properties" system-properties-mode="FALLBACK" /><!--2. 配置数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置 Configuration--><bean id="configuration" class="org.apache.ibatis.session.Configuration"><!-- 指定日志工具 --><property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl" /><!-- 配置缓存 --><property name="cacheEnabled" value="true" /></bean><!--3. 配置SqlSessionFactoryBean 对象--><bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 配置数据源 --><property name="dataSource" ref="dataSource" /><!-- 配置包下类别名 多个包之间使用 逗号分隔 --><property name="typeAliasesPackage" value="com.bjpowernode.domain" /><!-- 配置插件 --><property name="plugins"><array><bean id="pageInterceptor" class="com.github.pagehelper.PageInterceptor" /></array></property><!-- 注入configuration --><property name="configuration" ref="configuration" /><!-- 配置映射文件 --><property name="mapperLocations" value="mapper/**/*Mapper.xml"></property></bean><!-- 4.扫描所有的Mapper接口 自动创建Mapper的代理对象 --><bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 待扫描的mapper 接口的包名 多个之间使用逗号分隔 --><property name="basePackage" value="com.bjpowernode.mapper" /><!--注入SqlSessionFactoryBean 用于创建SqlSession --><!--SqlSessionFactoryBean 是可以自动装配的 但是 如果存在多个数据源时,可以指定SqlSessionFactoryBean区分数据源--><property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" /></bean><!-- 事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 配置数据源 指定要管理那个数据源的事务 --><property name="dataSource" ref="dataSource" /></bean><!-- 开启组件扫描 --><context:component-scan base-package="com.*" /><!-- 开启事务注解 --><tx:annotation-driven transaction-manager="transactionManager" />
</beans>
6.2.2 service代码
package com.bjpowernode.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements IUserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User queryUser(Integer id) {return userMapper.selectById(id);}@Overridepublic PageInfo<User> queryPage(Integer page,Integer limit) {Page<User> users = PageHelper.startPage(page, limit);userMapper.selectAll();return users.toPageInfo();}/*** 在spring中 不要在事务相关方法中 使用catch 处理* 如果处理了 异常不会被spring aop 增强的方法 捕获到 不会回滚* 事务配置就失效了 所以如果有异常 :* 1. 直接抛* 2. 在catch继续抛出异常* @param user* @return*/@Override//@Transactional(rollbackFor = Exception.class)public Integer addUser(User user) {int m = userMapper.insert(user);int n = 0;try {m = m/n;//出现异常} catch (Exception exception) {exception.printStackTrace();throw new RuntimeException("发生 请求回滚!!!");}return m;}
}
7
注意:
事务的传播行为,是指事务会发生传递。例如:A方法存在事务,A调用B方法,那么B方法也会在事务中执行,这种就是事务的传播行为。
package com.bjpowernode.controller;import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;@Controller
public class UserController {@Autowiredprivate IUserService userService;@Autowiredprivate UserMapper userMapper;@Transactionalpublic void add(User user){User user1 = new User();user1.setUsername("韩梅梅");user1.setPassword("123");user1.setRealname("韩梅梅");userMapper.insert(user1);//在父事务中添加数据 自己的程序 更新数据状态try {//其他的业务操作userService.addUser(user);//子事务 子事务发生异常}catch (Exception e){e.printStackTrace();}int n = 0;//n = 100/n;//出现异常}
}package com.bjpowernode.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.bjpowernode.domain.User;
import com.bjpowernode.mapper.UserMapper;
import com.bjpowernode.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserServiceImpl implements IUserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User queryUser(Integer id) {return userMapper.selectById(id);}@Overridepublic PageInfo<User> queryPage(Integer page,Integer limit) {Page<User> users = PageHelper.startPage(page, limit);userMapper.selectAll();return users.toPageInfo();}/*** 在spring中 不要在事务相关方法中 使用catch 处理* 如果处理了 异常不会被spring aop 增强的方法 捕获到 不会回滚* 事务配置就失效了 所以如果有异常 :* 1. 直接抛* 2. 在catch继续抛出异常* @param user* @return*/@Override//@Transactional(propagation = Propagation.SUPPORTS) //有就在事务中执行 没有就不在事务中执行//@Transactional(propagation = Propagation.MANDATORY) //必须要有事务//@Transactional(propagation = Propagation.NEVER) // 不在事务中执行 当前存在事务就报错@Transactional(propagation = Propagation.NESTED) // 当前存在一个事务 就 创建一个子事务 嵌套在当前事务中//外层事务出现异常会回滚子事务 子事务出现异常 不会回滚外层事务public Integer addUser(User user) {int m = userMapper.insert(user);int n = 0;m = m/n;//出现异常return m;}
}